web-dev-qa-db-fra.com

Comment fonctionne getSystemService () exactement?

À première vue dans le code ci-dessous, l'objet mLocationManager doit sortir de la portée une fois que onCreate(...) est terminé, et le comportement attendu est que onLocationChanged n'est jamais appelé ou appelé quelques-uns fois jusqu'à ce que l'objet soit ramassé. Cependant, l'objet renvoyé par le getSystemService semble être un singleton qui vit en dehors de la portée de MainActivity (de manière appropriée car il s'agit d'un service système :))

Après avoir pris un vidage de tas et l'avoir parcouru avec l'analyseur de mémoire Eclipse, il semble que ContextImpl conserve une référence à une instance de LocationManager. Dans le vidage de la mémoire, il y avait deux références à un objet LocationManager tandis que dans le code, il n'y en a clairement qu'une, ce qui signifie qu'une autre référence est créée ailleurs.

Mes questions sont:

Quelqu'un a-t-il une description complète de ce qui se passe exactement lors de l'appel de la mise en œuvre de:

public abstract Object getSystemService(String name);

l'objet est-il retourné un singleton créé paresseusement et où exactement la référence est-elle créée/conservée?

package com.neusoft.bump.client.storage;

import Android.location.Location;
import Android.location.LocationListener;
import Android.location.LocationManager;
import Android.os.Bundle;
import Android.app.Activity;
import Android.content.Context;
import Android.util.Log;
import Android.view.Menu;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.v("TAG", "STARTED");
        LocationManager mLocationManager = (LocationManager) this
                .getSystemService(Context.LOCATION_SERVICE);

        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                Log.v("TAG", "onLocationChanged");
                Log.v("TAG", "Latitude: " + location.getLatitude()
                        + "Longitude: " + location.getLongitude());
            }

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {}

            public void onProviderEnabled(String provider) {}

            public void onProviderDisabled(String provider) {}

        };

        // Register the listener with the Location Manager to receive location
        // updates
        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                600, 0, locationListener);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}

pdate1

LocationManager est créé en tant que singleton

private LocationManager getLocationManager() {
    synchronized (sSync) {
        if (sLocationManager == null) {
            IBinder b = ServiceManager.getService(LOCATION_SERVICE);
            ILocationManager service = ILocationManager.Stub.asInterface(b);
            sLocationManager = new LocationManager(service);
        }
    }
    return sLocationManager;
}

mais j'ai du mal à comprendre ce qui se passe lors de l'appel de ServiceManager.getService(LOCATION_SERVICE); même après avoir lu le code ServiceManager.

25
Vasile Jureschi

Voyez si ma discussion a du sens ...

dissection de Android service interne

Comme l'a suggéré l'un des lecteurs, j'essaie de copier une partie de l'article ici.

Vous êtes-vous déjà demandé comment une application peut gérer les services système tels que POWER MANAGER ou ACTIVITY MANAGER ou LOCATION MANAGER et plusieurs autres comme ceux-ci. Pour savoir que j'ai creusé dans le code source de Android et découvert comment cela se fait en interne. Alors, je vais commencer par le côté application Java code.

Du côté de l'application, nous devons appeler la fonction getService et transmettre l'ID du service système (par exemple POWER_SERVICE) pour obtenir un descripteur du service.

Voici le code pour getService défini dans /frameworks/base/core/Java/Android/os/ServiceManager.Java

    /**
44     * Returns a reference to a service with the given name.
45     *
46     * @param name the name of the service to get
47     * @return a reference to the service, or <code>null</code> if the service doesn't exist
48     */
49    public static IBinder getService(String name) {
50        try {
51            IBinder service = sCache.get(name);
52            if (service != null) {
53                return service;
54            } else {
55                return getIServiceManager().getService(name);
56            }
57        } catch (RemoteException e) {
58            Log.e(TAG, "error in getService", e);
59        }
60        return null;
61    }

Supposons que nous n’ayons pas le service dans le cache. Par conséquent, nous devons nous concentrer sur la ligne 55 return getIServiceManager().getService(name);

Cet appel obtient en fait un handle vers le gestionnaire de services et lui demande de renvoyer une référence du service dont nous avons passé le nom en paramètre.

Voyons maintenant comment la fonction getIServiceManager() renvoie un descripteur au ServiceManager.

Voici le code de getIserviceManager () de /frameworks/base/core/Java/Android/os/ServiceManager.Java

private static IServiceManager getIServiceManager() {
34        if (sServiceManager != null) {
35            return sServiceManager;
36        }
37
38        // Find the service manager
39        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
40        return sServiceManager;
41    }

Le ServicemanagerNative.asInterface () ressemble à ceci:

/**
28     * Cast a Binder object into a service manager interface, generating
29     * a proxy if needed.
30     */
31    static public IServiceManager asInterface(IBinder obj)
32    {
33        if (obj == null) {
34            return null;
35        }
36        IServiceManager in =
37            (IServiceManager)obj.queryLocalInterface(descriptor);
38        if (in != null) {
39            return in;
40        }
41
42        return new ServiceManagerProxy(obj);
43    }

Donc, fondamentalement, nous obtenons une poignée pour le gestionnaire de services natif.

Cette fonction asInterface est réellement enterrée dans les deux macros DECLARE_META_INTERFACE(ServiceManager) et IMPLEMENT_META_INTERFACE(ServiceManager, "Android.os.IServiceManager"); définies respectivement dans IserviceManager.h et IServiceManager.cpp.

Permet de plonger dans les deux macros définies dans /frameworks/base/include/binder/IInterface.h

La macro DECLARE_META_INTERFACE(ServiceManager) est définie comme

// ----------------------------------------------------------------------
73
74#define DECLARE_META_INTERFACE(INTERFACE)                               \
75    static const Android::String16 descriptor;                          \
76    static Android::sp<I##INTERFACE> asInterface(                       \
77            const Android::sp<Android::IBinder>& obj);                  \
78    virtual const Android::String16& getInterfaceDescriptor() const;    \
79    I##INTERFACE();                                                     \
80    virtual ~I##INTERFACE();                                            \

Et la IMPLEMENT_META_INTERFACE(ServiceManager, "Android.os.IServiceManager"); a été définie comme suit:

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
84    const Android::String16 I##INTERFACE::descriptor(NAME);             \
85    const Android::String16&                                            \
86            I##INTERFACE::getInterfaceDescriptor() const {              \
87        return I##INTERFACE::descriptor;                                \
88    }                                                                   \
89    Android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
90            const Android::sp<Android::IBinder>& obj)                   \
91    {                                                                   \
92        Android::sp<I##INTERFACE> intr;                                 \
93        if (obj != NULL) {                                              \
94            intr = static_cast<I##INTERFACE*>(                          \
95                obj->queryLocalInterface(                               \
96                        I##INTERFACE::descriptor).get());               \
97            if (intr == NULL) {                                         \
98                intr = new Bp##INTERFACE(obj);                          \
99            }                                                           \
100        }                                                               \
101        return intr;                                                    \
102    }                                                                   \
103    I##INTERFACE::I##INTERFACE() { }                                    \
104    I##INTERFACE::~I##INTERFACE() { }

Donc, si nous remplaçons ces deux macros dans le fichier IServiceManager.h & IServiceManager.cpp avec les paramètres de remplacement appropriés, elles ressemblent à ceci:

class IServiceManager : public IInterface
{
public:
   static const Android::String16 descriptor;  
    static Android::sp<IServiceManager> asInterface( const Android::sp<Android::IBinder>& obj);  
    virtual const Android::String16& getInterfaceDescriptor() const; 
    IServicemanager();  
    virtual ~IServiceManager();  
…......
….....
…...
…..

Et dans IServiceManager.cpp

const Android::String16 IServiceManager::descriptor("Android.os.IServiceManager”);             
const Android::String16&  
       IServiceManager::getInterfaceDescriptor() const {  
    return  IServiceManager::descriptor;
}    
Android::sp<IServiceManager> IServiceManager::asInterface(   
        const Android::sp<Android::IBinder>& obj)  
{   
    Android::sp< IServiceManager> intr;    
    if (obj != NULL) {     
        intr = static_cast<IServiceManager*>(   
            obj->queryLocalInterface(  
                    IServiceManager::descriptor).get());    
        if (intr == NULL) {   
            intr = new BpServiceManager(obj);  
        }  
    }     
    return intr;    
}     
IServiceManager::IServiceManager() { }    
IServiceManager::~IIServiceManager { } 

Donc, si vous voyez la ligne 12 qui indique si le gestionnaire de services est en cours d'exécution (et cela devrait parce que le gestionnaire de services démarre dans le processus d'initialisation pendant Android démarrage), il lui renvoie la référence via la fonction queryLocalinterface et il monte jusqu'à l'interface Java.

public IBinder getService(String name) throws RemoteException {
116        Parcel data = Parcel.obtain();
117        Parcel reply = Parcel.obtain();
118        data.writeInterfaceToken(IServiceManager.descriptor);
119        data.writeString(name);
120        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
121        IBinder binder = reply.readStrongBinder();
122        reply.recycle();
123        data.recycle();
124        return binder;
125    }

de ServiceManagerNative.Java. Dans cette fonction, nous transmettons le service que nous recherchons.

Et la fonction onTransact pour GET_SERVICE_TRANSACTION sur le stub distant ressemble à ceci:

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
51    {
52        try {
53            switch (code) {
54            case IServiceManager.GET_SERVICE_TRANSACTION: {
55                data.enforceInterface(IServiceManager.descriptor);
56                String name = data.readString();
57                IBinder service = getService(name);
58                reply.writeStrongBinder(service);
59                return true;
60            }
61
62            case IServiceManager.CHECK_SERVICE_TRANSACTION: {
63                data.enforceInterface(IServiceManager.descriptor);
64                String name = data.readString();
65                IBinder service = checkService(name);
66                reply.writeStrongBinder(service);
67                return true;
68            }
69
//Rest has been discarded for brevity…………………..

………………….
………………….
…………………

Il renvoie la référence au service requis via la fonction getService. La fonction getService de /frameworks/base/libs/binder/IServiceManager.cpp ressemble à ceci:

  virtual sp<IBinder> getService(const String16& name) const
134    {
135        unsigned n;
136        for (n = 0; n < 5; n++){
137            sp<IBinder> svc = checkService(name);
138            if (svc != NULL) return svc;
139            LOGI("Waiting for service %s...\n", String8(name).string());
140            sleep(1);
141        }
142        return NULL;
143    }

Il vérifie donc si le service est disponible, puis lui renvoie une référence. Ici, je voudrais ajouter que lorsque nous renvoyons une référence à un objet IBinder, contrairement aux autres types de données, il n'est pas copié dans l'espace d'adressage du client, mais c'est en fait la même référence de l'objet IBinder qui est partagée avec le client via un technique spéciale appelée mappage d'objets dans le pilote Binder.

Pour ajouter plus de détails à la discussion, permettez-moi d'aller un peu plus loin.

La fonction checkService ressemble à ceci:

virtual sp<IBinder> checkService( const String16& name) const

    {
        Parcel data, reply;

        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

        data.writeString16(name);

        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);

        return reply.readStrongBinder();

    }

Donc, il appelle en fait un service distant et lui passe le code CHECK_SERVICE_TRANSACTION (sa valeur enum de 2).

Ce service distant est en fait implémenté dans frameworks/base/cmds/servicemanager/service_manager.c et son onTransact ressemble à ce qui suit.

switch(txn->code) {
   case SVC_MGR_GET_SERVICE:
           case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        ptr = do_find_service(bs, s, len);
        if (!ptr)
            break;
        bio_put_ref(reply, ptr);
        return 0;

Par conséquent, nous finissons par appeler la fonction nommée do_find_service qui obtient une référence au service et la renvoie.

Le do_find_service du même fichier se présente comme suit:

void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)

{

    struct svcinfo *si;

    si = find_svc(s, len);



//    ALOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);

    if (si && si->ptr) {

        return si->ptr;

    } else {

        return 0;

    }

find_svc ressemble à ceci:

struct svcinfo *find_svc(uint16_t *s16, unsigned len)

{

    struct svcinfo *si;



    for (si = svclist; si; si = si->next) {

        if ((len == si->len) &&

            !memcmp(s16, si->name, len * sizeof(uint16_t))) {

            return si;

        }

    }

    return 0;

}

Comme il devient clair qu'il traverse la liste svclist et renvoie le service que nous recherchons.

54

mais j'ai du mal à comprendre ce qui se passe lors de l'appel de ServiceManager.getService (LOCATION_SERVICE); même après avoir lu le code ServiceManager.

Ok, voici donc le code source de getService () dans ServiceManager.Java :

public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

Comme nous pouvons le voir, si le service demandé n'est pas encore mis en cache, cela appelle getIServiceManager().getService(name). getIServiceManager () est une méthode de la même classe (nous allons voir getService (nom) à l'étape suivante):

private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
        return sServiceManager;
    }

    // Find the service manager
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
    return sServiceManager;
}

Donc, cela nous envoie essentiellement à ServiceManagerNative.Java où nous devons rechercher getService (nom):

public IBinder getService(String name) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IServiceManager.descriptor);
    data.writeString(name);
    mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
    IBinder binder = reply.readStrongBinder();
    reply.recycle();
    data.recycle();
    return binder;
}

Qui initie une transaction pour récupérer un service qui a un nom "LOCATION_SERVICE".

Il devient de plus en plus difficile de partir d'ici en raison d'une structure complexe de classes et d'interfaces qui traitent des choses de bas niveau, comme les services système. Mais en gros, tout est fait dans Context.Java, ContextImpl.Java, ServiceManager.Java et ServiceManagerNative.Java. Notez également que certains d'entre eux peuvent conserver des caches ou des mappes locaux avec des références aux instances de service (c'est-à-dire que vous pouvez voir une sCache.get(name) dans la liste ServiceManager.Java ci-dessus), c'est de là que peuvent provenir des références supplémentaires.

Je ne pense pas que vous obtiendrez une réponse plus détaillée ici sur StackOverflow, car elle devient de très bas niveau. Vous voudrez peut-être demander quelque part comme sur une Android OS qui a des employés de Google dessus).

6
Anton Cherkashyn

Méthode getSystemService

public abstract Object getSystemService(String name);

Est implémenté dans https://Android.googlesource.com/platform/frameworks/base/+/Android-5.0.2_r1/core/Java/Android/app/ContextImpl.Java

@Override
    public Object getSystemService(String name) {
        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
        return fetcher == null ? null : fetcher.getService(this);
    }

Où SYSTEM_SERVICE_MAP est:

private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
            new HashMap<String, ServiceFetcher>();

et tous les services sont enregistrés dans un bloc statique

static {

avec appel à registerService comme ceci:

 registerService(LOCATION_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    IBinder b = ServiceManager.getService(LOCATION_SERVICE);
                    return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
                }});

ou

registerService(INPUT_SERVICE, new StaticServiceFetcher() {
                public Object createStaticService() {
                    return InputManager.getInstance();
                }});

ServiceFetcher et StaticServiceFetcher implémentent un modèle de chargement paresseux.

2
Mihail

Le gestionnaire d'emplacement, car la plupart des services/gestionnaires du système est créé à un stade précoce pendant le processus de démarrage.

app_process est le composant natif qui démarre le DalvikVM, en plus, il dit à ZigoteInit (la classe qui fait le travail réel) de lancer le SystemServer. C'est ici que la première instance de LocationManager est créée et où la référence est conservée sur le ServerThread.

/frameworks/base/services/Java/com/Android/server/SystemServer.Java

DevicePolicyManagerService devicePolicy = null;
StatusBarManagerService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
WallpaperManagerService wallpaper = null;
-> LocationManagerService location = null;
CountryDetectorService countryDetector = null;
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;

try {
    Slog.i(TAG, "Location Manager");
    location = new LocationManagerService(context);
    ServiceManager.addService(Context.LOCATION_SERVICE, location);
} catch (Throwable e) {
    reportWtf("starting Location Manager", e);
}

Le reste vous est déjà connu, je pense.

2
AntonyMCs