web-dev-qa-db-ja.com

getSystemService()はどのように正確に機能しますか?

mLocationManagerオブジェクトの下のコードを一見すると、onCreate(...)の終了後にスコープ外になるはずです。予想される動作は、onLocationChangedが呼び出されたり呼び出されたりしないことです。オブジェクトがガベージコレクションされるまでの時間。ただし、getSystemServiceによって返されるオブジェクトは、MainActivityのスコープ外にあるシングルトンのようです(システムサービスであるため、適切に:))

ヒープダンプを取得してEclipse Memory Analyzerで処理した後、ContextImplがLocationManagerインスタンスへの参照を保持しているようです。メモリダンプでは、LocationManagerオブジェクトへの参照が2つありましたが、コードでは明らかに1つしかありません。つまり、別の参照が別の場所に作成されています。

私の質問は:

誰かが次の実装を呼び出すときに正確に何が起こっているかについての完全な説明がありますか?

_public abstract Object getSystemService(String name);
_

オブジェクトは遅延して作成されたシングルトンを返し、どこに参照が作成/保持されますか?

_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はシングルトンとして作成されます

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

しかし、ServiceManagerコードを読んだ後でも、ServiceManager.getService(LOCATION_SERVICE);を呼び出すとどうなるか理解できません。

25
Vasile Jureschi

私の議論が理にかなっているかどうかを確認してください...

Android service internal の分析

読者の一人が示唆したように、私はここに記事の一部をコピーしようとしています。

アプリがPOWER MANAGERやACTIVITY MANAGERやLOCATION MANAGERなどのシステムサービスやこれらのような他のいくつかのシステムサービスをどのように取得するのか疑問に思ったことはありませんか。 Androidのソースコードを調べて、これが内部でどのように行われているかを確認しました。アプリケーション側のJavaコードから始めましょう。

アプリケーション側では、関数getServiceを呼び出し、システムサービスのID(POWER_SERVICEなど)を渡して、サービスへのハンドルを取得する必要があります。

/frameworks/base/core/Java/Android/os/ServiceManager.Javaで定義されているgetServiceのコードは次のとおりです

_    /**
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    }
_

キャッシュにサービスがないとします。したがって、55行に集中する必要がありますreturn getIServiceManager().getService(name);

この呼び出しは実際にサービスマネージャーへのハンドルを取得し、パラメーターとして渡した名前を持つサービスの参照を返すように要求します。

ここで、getIServiceManager()関数がServiceManagerにハンドルを返す方法を見てみましょう。

/frameworks/base/core/Java/Android/os/ServiceManager.JavaのgetIserviceManager()のコードは次のとおりです

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

ServicemanagerNative.asInterface()は次のようになります。

_/**
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    }
_

したがって、基本的にはネイティブのservicemanagerへのハンドルを取得しています。

このasInterface関数は、実際には、それぞれIserviceManager.hおよびIServiceManager.cppで定義されている2つのマクロDECLARE_META_INTERFACE(ServiceManager)およびIMPLEMENT_META_INTERFACE(ServiceManager, "Android.os.IServiceManager");の中に埋め込まれています。

/frameworks/base/include/binder/IInterface.hで定義されている2つのマクロを詳しく見てみましょう

DECLARE_META_INTERFACE(ServiceManager)マクロは次のように定義されます

_// ----------------------------------------------------------------------
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();                                            \
_

また、IMPLEMENT_META_INTERFACE(ServiceManager, "Android.os.IServiceManager");は次のように定義されています。

_#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() { }
_

したがって、IServiceManager.hとIServiceManager.cppファイルのこれら2つのマクロを適切な置換パラメーターに置き換えて展開すると、次のようになります。

_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();  
…......
….....
…...
…..
_

そして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 { } 
_

したがって、サービスマネージャーが起動して実行されているかどうかを示す12行目が表示された場合(サービスマネージャーがAndroid起動中に)initプロセスで起動したため)への参照が返されますqueryLocalinterface関数を使用すると、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    }
_

serviceManagerNative.Javaから。この関数では、探しているサービスを渡します。

また、リモートスタブのGET_SERVICE_TRANSACTIONのonTransact関数は次のようになります。

_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…………………..

………………….
………………….
…………………
_

関数getServiceを介して、必要なサービスへの参照を返します。 /frameworks/base/libs/binder/IServiceManager.cppのgetService関数は次のようになります。

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

そのため、実際にはサービスが利用可能かどうかを確認し、サービスへの参照を返します。ここで、IBinderオブジェクトへの参照を返すときに、他のデータ型とは異なり、クライアントのアドレス空間にコピーされないことを追加したいと思いますが、実際には、クライアントを通じて共有されるIBinderオブジェクトの同じ参照です。バインダードライバーでオブジェクトマッピングと呼ばれる特別な手法。

ディスカッションに詳細を追加するために、もう少し詳しく説明します。

CheckService関数は次のようになります。

_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();

    }
_

そのため、実際にはリモートサービスを呼び出して、CHECK_SERVICE_TRANSACTIONコード(enum値は2)を渡します。

このリモートサービスは、実際にはframeworks/base/cmds/servicemanager/service_manager.cに実装されており、onTransactは次のようになります。

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

したがって、サービスへの参照を取得してそれを返すdo_find_serviceという名前の関数を呼び出すことになります。

同じファイルのdo_find_serviceは次のようになります。

_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は次のようになります。

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

}
_

Svclistを通過し、探しているサービスを返すことが明らかになったとき。

54

serviceManager.getService(LOCATION_SERVICE)を呼び出すとどうなるか理解できません。 ServiceManagerコードを読んだ後でも。

さて、これが ServiceManager.Java のgetService()のソースコードです。

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

ご覧のとおり、要求されたサービスがまだキャッシュされていない場合、これはgetIServiceManager().getService(name)を呼び出します。 getIServiceManager()は同じクラスのメソッドです(次のステップでgetService(name)に移動します):

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

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

したがって、これは基本的に ServiceManagerNative.Java に送信し、ここで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;
}
_

これにより、「LOCATION_SERVICE」という名前のサービスを取得するためのトランザクションが開始されます。

システムサービスなどの低レベルのものを扱うクラスとインターフェースの複雑な構造のため、ここからの移動が難しくなっています。ただし、基本的にはすべてContext.Java、ContextImpl.Java、ServiceManager.Java、ServiceManagerNative.Javaで行われます。また、それらの一部はローカルキャッシュまたはサービスインスタンスへの参照を含むマップを保持する可能性があることにも注意してください(つまり、上記のServiceManager.JavaリストでsCache.get(name)を確認できます)。ここで追加の参照が行われます。

StackOverflowでは非常に低レベルになるため、ここでより詳細な回答が得られるとは思いません。 Android OSメーリングリストにgoogle従業員が含まれているような場所で質問したい場合があります。

6

メソッドgetSystemService

public abstract Object getSystemService(String name);

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

SYSTEM_SERVICE_MAPは次のとおりです。

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

そして、すべてのサービスは静的ブロックに登録されています

static {

次のように、registerServiceを呼び出します。

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

または

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

ServiceFetcherとStaticServiceFetcherは遅延読み込みパターンを実装します。

2
Mihail

ほとんどのシステムサービス/マネージャーは、ブートプロセスの初期段階で作成されるため、ロケーションマネージャー。

app_processは、DalvikVMを起動するネイティブコンポーネントです。さらに、ZigoteInit(実際の作業を行うクラス)にSystemServerを起動するように指示します。ここに、LocationManagerの最初のインスタンスが作成され、参照がその中の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);
}

残りはすでに知っていると思います。

2
AntonyMCs