Comment getsystemservice () fonctionne-t-il exactement?

À première vue dans le code ci-dessous, l'objet mLocationManager devrait sortir de la portée après la fin de onCreate(...), et le comportement attendu est que onLocationChanged n'est jamais appelé ou appelé plusieurs fois jusqu'à ce que l'objet soit récupéré. Cependant, l'objet retourné par le getSystemService semble être singleton qui vit en dehors de la portée de MainActivity (de manière appropriée puisque c'est 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 mémoire, il y avait deux références à un objet LocationManager alors que dans le code il n'y en a clairement qu'une seule, 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 l'implémentation de:

public abstract Object getSystemService(String name);

L'objet retourné est-il un singleton créé paresseusement et où est exactement la référence créés/maintenus ?

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;
    }
}

Update1

Le 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.

22
demandé sur Mr_and_Mrs_D 2013-02-28 17:52:39

4 réponses

Voyez si ma discussion a du sens...

Dissection du service android interne

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

Vous êtes-vous déjà demandé comment une application gère 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 D'Android et découvert comment cela se fait en interne. Alors laisse moi commencer à partir du code java du côté de l'application.

Du côté de l'application, nous devons appeler la fonction getService et passer L'ID du service système (disons POWER_SERVICE) pour obtenir un handle au service.

Voici le code de 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. Nous devons donc nous concentrer sur la ligne 55 return getIServiceManager().getService(name);

Cet appel reçoit en fait un handle au gestionnaire de services et lui demande de retourner une référence du service dont nous avons passé le nom en paramètre.

Voyons maintenant comment la fonction getIServiceManager() renvoie un handle 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 Gestionnaire De Service.asInterface() ressemble à ce qui suit:

/**
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 servicemanager natif.

Ce la fonction asInterface est en fait enterrée dans les deux macros DECLARE_META_INTERFACE(ServiceManager) et IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); défini dans IserviceManager.h et IServiceManager.rpc respectivement.

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 le IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); a été défini 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 développons ces deux macros dans IServiceManager.h & IServiceManager.fichier cpp avec le remplacement approprié paramètres ils ressemblent à ce qui suit:

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.rpc

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 montre si le Gestionnaire de Services est opérationnel (et cela devrait être le cas parce que le gestionnaire de services démarre dans le processus d'initialisation lors du démarrage D'Android), il 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 passons le service nous sommes à la recherche pour.

Et la fonction onTransact pour GET_SERVICE_TRANSACTION sur le talon distant ressemble à ce qui suit:

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 nécessaire via la fonction getService. La fonction getService de / frameworks / base / libs/binder / IServiceManager.rpc ressemble à ce qui suit:

  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 renvoie une référence à celui-ci. Ici, je voudrais ajouter que lorsque nous retournons un référence à un objet IBinder, contrairement à d'autres types de données, il n'est pas copié dans l'espace d'adressage du client, mais il s'agit en fait de la même référence de L'objet IBinder qui est partagée avec le client via une 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 à ce qui suit:

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 réellement un service distant et passe Code CHECK_SERVICE_TRANSACTION (c'est une valeur enum de 2).

Ce service distant est réellement 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 le 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 à suit:

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 le svclist et retourne le service que nous recherchons.

45
répondu somenath mukhopadhyay 2014-07-24 14:15:08

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, alors voici 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 dans la même classe(nous allons het pour getService(name) dans l'étape suivante):

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

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

Donc ceci fondamentalement nous envoie à ServiceManagerNative.java où nous devons chercher getService (name):

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 plus difficile de passer 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 fondamentalement, tout est fait dans le contexte.java, ContextImpl.java, ServiceManager.java et ServiceManagerNative.Java. Notez également que certains des ils peuvent conserver des caches locaux ou des cartes avec des références à des instances de service (c'est-à-dire que vous pouvez voir un sCache.get(name) dans le ServiceManager.java listing ci-dessus), c'est de là que des références supplémentaires peuvent provenir.

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

6
répondu Anton Cherkashyn 2013-02-28 18:58:12

Le Gestionnaire D'emplacement, comme la plupart des services/gestionnaires 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 outre, il indique à 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 en son sein.

/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 est déjà connu pour vous, je pense.

2
répondu AntonyMCs 2013-09-01 10:00:40

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 le bloc statique

static {

Avec un 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
répondu Mihail 2018-04-23 21:26:54