fwrite

fwrite

好好生活
twitter
github
email

サービス

サービスの概要#

サービスの特徴
サービスは AMS が管理するオブジェクト です。
サービスはアクティビティに従わずに単独で起動できます(ただし、そのサービスに属するプロセス、つまり ActivityThread も起動する必要があります)。
AMS は ActivityThread にサービスを起動するよう通知します。
一般的にアプリがサービスを起動する方法は 2 つあります。
Context#startService を使用して指定されたサービスを起動する
Context#bindService を使用してサービスをバインドする

サービスのバインディング#

public class TestService extends AppCompatActivity {
    // 匿名クラス
    private ServiceConnection serviceConnection = new ServiceConnection() {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = new Intent(this, MyService.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }
    
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // ダウンキャスト(もちろんBinderが提供するメソッドを使用することもできます)
            MyService.MyBindClass bindClass = (MyService.MyBindClass) iBinder;	
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };
}

ContextImpl - bindService が AMS にレシーバーを登録する#

Context の実装クラスは ContextImpl です(理由は Activity#attach 関数を見ればわかりますが、ここでは詳しく説明しません)。したがって、bindService を使用する際の実装クラスは ContextImpl です。
IServiceConnection オブジェクトを取得LoadApk#getServiceDispatcher を通じて IServiceConnection オブジェクトを取得します。IServiceConnection はアプリ側の IBinder サーバーオブジェクトであり、将来的に AMS がコールバックを行います(次の小節で説明します)。
Binder プロキシを取得して AMS にアクセスActivityManager#getService で Binder プロキシクラスを取得し、プロキシクラスを通じて AMS#bindIsolatedService メソッドを呼び出します。

// ContextImpl.java

    final @NonNull LoadedApk mPackageInfo;

    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        ...

        return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
                getUser());
    }


    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            String instanceName, Handler handler, Executor executor, UserHandle user) {

        IServiceConnection sd;

        // 判空
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (handler != null && executor != null) {
            throw new IllegalArgumentException("Handler and Executor both supplied");
        }

        // mPackageInfoの型はLoadedApkです
        if (mPackageInfo != null) {

            // @ 1. 主にLoadedApk#getServiceDispatcherを通じてIServiceConnectionをラップします
            if (executor != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
            } else {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            }
        } else {
            throw new RuntimeException("Not supported in system context");
        }

        validateServiceIntent(service);
        try {

            ... 省略部分

                // @ 2. AMS#bindIsolatedServiceメソッドを分析します
            int res = ActivityManager.getService().bindIsolatedService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, instanceName, getOpPackageName(), user.getIdentifier());

            // バインド結果
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

IServiceConnection の作成 - Binder コールバック - サービス接続#

LoadedApk#getServiceDispatcherCommon メソッドを分析すると、主に IServiceConnection インターフェースを作成していることがわかります。このインターフェースは後で AMS がコールバックを行います。
キャッシュをチェック:まず、ローカルキャッシュにそのサービスの Binder コールバックがあるかどうかを確認します(Context がキーとして使用されます)。もしあれば、直接返します。なければ、ServiceDispatcher オブジェクトを作成します。
もしなければ、LoadedApk.ServiceDispatcher オブジェクトを作成します。
getIServiceConnection を通じて Binder サーバーコールバックインターフェースを取得します。

// LoadedApk.java

public final class LoadedApk {

    // ServiceDispatcherのキャッシュ
    private final ArrayMap<Context, ArrayMap<ServiceConnection, 
        LoadedApk.ServiceDispatcher>> mServices = new ArrayMap<>();

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        // @ getServiceDispatcherCommonを追跡
        return getServiceDispatcherCommon(c, context, handler, null, flags);
    }


    private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
            Context context, Handler handler, Executor executor, int flags) {

        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = 
                mServices.get(context);

            // 1. キャッシュを判断
            if (map != null) {
                ...

                sd = map.get(c);    // キャッシュあり
            }

            if (sd == null) {
                // @ 2. キャッシュがない場合、直接作成します(返されたインターフェースに基づいて作成します)
                if (executor != null) {
                    sd = new ServiceDispatcher(c, context, executor, flags);
                } else {
                    sd = new ServiceDispatcher(c, context, handler, flags);
                }

                ...

                if (map == null) {
                    map = new ArrayMap<>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler, executor);
            }

            // 3. LoadedApk.ServiceDispatcher#getIServiceConnectionを分析
            return sd.getIServiceConnection();
        }

    }
}

ServiceDispatcher クラス:LoadedApk の静的内部クラスで、AMS にコールバックを行う Binder は InnerConnection クラス です。AMS がコールバックを行うと、connected メソッドが呼び出されます。
IServiceConnection.Stub を作成して AMS にコールバックを行います。
AMS のコールバックは、RunConnection オブジェクトを作成し、Handler または Executor を呼び出します。
アプリ側の IServiceConnection サービス実装、クラス関係図

// LoadedApk.java

public final class LoadedApk {

    static final class ServiceDispatcher {

        private final ServiceDispatcher.InnerConnection mIServiceConnection;

        private static class InnerConnection extends IServiceConnection.Stub {

            // 弱参照
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            // AMSが呼び出すとconnectedメソッドが使用されます
            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    // @ connectedを分析
                    sd.connected(name, service, dead);
                }
            }
        }

        // コンストラクタ
        ServiceDispatcher(ServiceConnection conn,
                    Context context, Handler activityThread, int flags) {

            // InnerConnectionオブジェクトを作成
            mIServiceConnection = new InnerConnection(this);

            ... 省略部分

        }

        IServiceConnection getIServiceConnection() {
            // 実装クラスはIServiceConnection.Stubを実装しています
            return mIServiceConnection;
        }

        public void connected(ComponentName name, IBinder service, boolean dead) {
            // Executorが設定されている場合はExecutorを通じて通知します
            if (mActivityExecutor != null) {
                // Executorを通じて
                mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
            } else if (mActivityThread != null) {
                // @ Handlerを通じて(ActivityThread#Hクラス)
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }

    }
}

AMS を通じた Binder コールバック - ServiceConnection#

RunConnection クラス:Runnable で、現在の状況では Handler を使用しています。この Runnable はActivityThread#Hクラス(これも Handler です)に渡され、コールバックが行われます。

// LoadedApk#ServiceDispatcher#RunConnection.java

    private final class RunConnection implements Runnable {

        // 構築
        RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
            mName = name;
            mService = service;
            mCommand = command;
            mDead = dead;
        }

        public void run() {
            // 現在渡されているmCommand = 0
            if (mCommand == 0) {
                // @ doConnectedメソッドを分析
                doConnected(mName, mService, mDead);
            } else if (mCommand == 1) {
                doDeath(mName, mService);
            }
        }

        final ComponentName mName;
        final IBinder mService;
        final int mCommand;
        final boolean mDead;
    }

ServiceDispatcher#doConnected メソッド:アプリに渡された ServiceConnection インターフェースにコールバックを行い、以下のような複数の状況があります。

同名のサーバーキャッシュ目標 IBinder の状態説明処理行動
Y比較相同既に接続済み直接返し、特に処理しない
Y比較不相同既に接続済みだが異なる IBinder 代理を取得1. まず切断 onServiceDisconnected2. 再接続 onServiceConnected
NIBinder が空でない初めての接続接続呼び出し onServiceConnected
// LoadedApk#ServiceDispatcher.java

    private final ServiceConnection mConnection;

    public void doConnected(ComponentName name, IBinder service, boolean dead) {
        ServiceDispatcher.ConnectionInfo old;
        ServiceDispatcher.ConnectionInfo info;

        synchronized (this) {
            ...

            old = mActiveConnections.get(name);
            // 1. すでにバインドされているかつ同じBinderかを判断
            if (old != null && old.binder == service) {
                // すでにこの接続があります。まあいいでしょう!
                return;
            }

            if (service != null) {
                // 新しいサービスを接続開始
                info = new ConnectionInfo();
                info.binder = service;
                info.deathMonitor = new DeathMonitor(name, service);
                try {
                    service.linkToDeath(info.deathMonitor, 0);
                    mActiveConnections.put(name, info);
                } catch (RemoteException e) {
                    // このサービスは取得する前に死んでいました...何もしないでおきます。
                    mActiveConnections.remove(name);
                    return;
                }
            } else {
                // サービスが切断された場合、キャッシュから削除
                mActiveConnections.remove(name);
            }

            if (old != null) {
                old.binder.unlinkToDeath(old.deathMonitor, 0);
            }
        }
        // 2. もし古い接続であれば、onServiceDisconnectedを呼び出して切断しますが、異なるIBinderです
        if (old != null) {
            mConnection.onServiceDisconnected(name);
        }

        if (dead) {
            mConnection.onBindingDied(name);
        } else {
            // 新しい有効なサービスがあれば、今接続されています。
            if (service != null) {

                // 3. onServiceConnectedを呼び出します(ユーザーインターフェースに戻ります)
                mConnection.onServiceConnected(name, service);
            } else {
                mConnection.onNullBinding(name);
            }
        }
    }

AMS - サービスの起動、バインドプロセス#

クライアントアプリはContextImpl#bindServiceを通じて、AMS#bindIsolatedServiceメソッドをトリガーし、アプリとサービスをバインドします。
サービス Intent には FileDescriptors が必要です。
ActiveServices#bindServiceLocked メソッドを呼び出します。

// ActivityManagerService.java

    // 存活しているサービスのリスト
    final ActiveServices mServices;

    public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String instanceName,
            String callingPackage, int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");

        // 1. intentをチェック
        // intentにはFileDescriptorsが必要です
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        // クライアントパッケージをチェック
        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        ... 省略部分

        // 関数名にLockが含まれている理由は、呼び出しスレッドをブロックするためです
        synchronized(this) {

            // @ 2. ActiveServices#bindServiceLockedを呼び出します
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, instanceName, callingPackage, userId);
        }

    }

ActiveServices#bindServiceLocked メソッドは、呼び出し元プロセスが存在するかどうかを確認します(すでに kill されている可能性があります)。存在しない場合は例外をスローします。
retrieveServiceLocked メソッドを通じて:PKMS から対応するサービス情報を見つけます。
アプリ側で設定された BIND_AUTO_CREATE フラグが設定されている場合、そのフラグを通じて bringUpServiceLocked メソッドで 自動的にサービスを作成します
requestServiceBindingLocked メソッドを通じて:サービスをバインドするよう要求します。この関数には初回バインドと再バインドの機能が含まれています(違いは最後のパラメータにあります)。

// ActiveServices.java

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String instanceName, String callingPackage, final int userId)
            throws TransactionTooLargeException {

        // 呼び出し元のPid、Uid
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();

        // 1. AMSを通じて呼び出し元プロセスを探します
        final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);

        if (callerApp == null) {
            // エラーをスロー
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + callingPid
                    + ") when binding service " + service);
        }

        ...

        // 2. PKMS内のサービスを探します
        ServiceLookupResult res =
            retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true,
                    callerFg, isBindExternal, allowInstant);

        // resがnullの場合、サービスが存在しないことを示します
        if (res == null) {
            return 0;
        }

        // その中のServiceRecordを判断します
        if (res.record == null) {
            return -1;
        }

        ServiceRecord s = res.record;

        ...

        try {
            ...

            // サービスを自動的に起動します
            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                needOomAdj = true;

                // サービスを自動的に起動します
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired, packageFrozen, true) != null) {
                    mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
                    return 0;
                }
            }

            ...

            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

            // サービス接続記録
            ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent,
                callerApp.uid, callerApp.processName, callingPackage);

            ... OomAdjの値を調整します

            // サービスプロセスが起動しているかどうかを判断します(appはProcessRecordです)
            if (s.app != null && b.intent.received) {

                try {
                   // ユーザーが渡したインターフェースを通じてコールバックを行います
				   // 前の小節で分析した通り(connはIServiceConnectです)、最終的にonServiceConnectedが呼び出されます
                    c.conn.connected(s.name, b.intent.binder, false);
                } /* 省略 catch */


                // 現在のアプリプロセスがサービスプロセスに最初にバインドされている場合
				//  && doRebindメソッドが呼び出された場合
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    // 最後のパラメータは再バインドするかどうかを示します
					//  @ requestServiceBindingLockedメソッドを分析します
                    requestServiceBindingLocked(s, b.intent, callerFg, true);
                }

            } else if (!b.intent.requested) {    // アプリ側でまだバインドされていない
                // 最後のパラメータは再バインドするかどうかを示します
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }

            ...

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }

ActiveServices#requestServiceBindingLocked メソッド:渡された ServiceRecord に対して判断、操作を行います。
ServiceRecord が指定されたプロセスが起動しているかどうかを確認します(ProcessRecord と IApplicationThread が存在するかどうかを確認します)。つまり、サービスが起動しているかどうかを確認します。
現在、サービスのターゲットプロセスがすでに起動していると仮定すると、そのプロセス内で実装されているIApplicationThread#requestServiceBindingLockedを通じてターゲットプロセス内に戻ります。

// ActiveServices.java

    private final boolean requestServiceBindingLocked(ServiceRecord r, 
          IntentBindRecord i,
          boolean execInFg, 
          boolean rebind) throws TransactionTooLargeException {

        // ProcessRecordとそのService#IApplicationThreadが存在するかどうかを確認します
        if (r.app == null || r.app.getThread() == null) {
            // サービスが現在実行されていない場合、まだバインドできません。
            return false;
        }

        ... デバッグメッセージ


        // クライアントがまだバインドしていないか、サービスを再バインドする必要がある場合
        if ((!i.requested || rebind) && i.apps.size() > 0) {

            try {
                // IApplicationThread(アプリ側の実装)にscheduleBindServiceメソッドを通知します
                
                // scheduleBindServiceメソッドを分析します
                r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.mState.getReportedProcState());

                if (!rebind) {
                    i.requested = true;
                }

                i.hasBound = true;
                i.doRebind = false;
            } /* 省略 catch */
        }

        return true;
    }

アプリプロセス - Service#onBind の呼び出し#

ActivityThread - scheduleBindService がサービスのあるプロセスでバインドを行います。
BindServiceData オブジェクトを作成し、ServiceRecordIntent 情報を保存します。
サーバープロセスが Binder 通信通知を受け取った後、Handler を通じてメインスレッドに BIND_SERVICE 情報を送信します。
メインスレッドが BIND_SERVICE メッセージを受け取ると、handleBindService メソッドを呼び出します。初回バインドの場合、1. サービスの onBind メソッドを呼び出し、次に 2. AMS#publishService メソッドを呼び出します。

// ActivityThread.java

    private class ApplicationThread extends IApplicationThread.Stub {

        ... 省略他のメソッド

        public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            ...

            // 1. BindServiceDataオブジェクトを作成し、ServiceRecordとIntentを保存します
            BindServiceData s = new BindServiceData();
            // tokenはServiceRecordです
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            ... デバッグメッセージ

            // 2. メインスレッドにBIND_SERVICE情報を送信します
            sendMessage(H.BIND_SERVICE, s);
        }
    }


    class H extends Handler {

        public static final int BIND_SERVICE = 121;

        public void handleMessage(Message msg) {

            ... 省略他のcase

            case BIND_SERVICE:
                ... 省略トレース

                handleBindService((BindServiceData)msg.obj);
                break;
        }
    }

    private void handleBindService(BindServiceData data) {
        // tokenはServiceRecordです
        CreateServiceData createData = mServicesData.get(data.token);

        // キャッシュを確認します
        // mServicesはhandleCreateService時に追加されます
        Service s = mServices.get(data.token);

        // 取得できない場合、サービスはまだ起動していないことを示します
        if (s != null) {
            try {
                ...

                try {
                    if (!data.rebind) {
                        // 3. 初回バインド時にonBind、AMS#publishServiceメソッドを呼び出します
                        IBinder binder = s.onBind(data.intent);

                        // publishServiceはサーバーが起動したことを示します
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        // 再バインドの場合はonRebindを呼び出します
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                try {
                    // AMSにこのサービスが実行中であることを通知します
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                }  /* 省略 catch */
            } /* 省略 catch */
        }
    }

サービスの公開 - すべてのユーザーに通知#

サービスプロセスが初めて起動する場合、AMS#publishService メソッドが呼び出されます。

// ActivityManagerService.java

    // tokenはBindServiceDataです
    public void publishService(IBinder token, Intent intent, IBinder service) {
        // 漏洩する可能性のあるファイルディスクリプタを拒否します
        if (intent != null && intent.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token");
            }
            // publishServiceLockedメソッドを分析します
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        }
    }

ActiveServices)#publishServiceLocked は、ServiceRecord を通じてそのサービスに接続しているすべてのアプリ側に通知を行い、逐次通知します。

// ActiveServices.java

    // tokenはBindServiceDataです
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {

        final long origId = Binder.clearCallingIdentity();
        try {
            ... デバッグメッセージ

            if (r != null) {
                ...

                // そのサービスのバインド情報を取得します
                IntentBindRecord b = r.bindings.get(filter);

                if (b != null && !b.received) {
                    b.binder = service;
                    b.requested = true;
                    b.received = true;

                    // ServiceRecordを通じて、そのサービスに接続しているすべての記録を取得します
                    ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();

                    for (int conni = connections.size() - 1; conni >= 0; conni--) {
                        ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
                        // 各接続記録を遍歴します
                        for (int i=0; i<clist.size(); i++) {
                            // 現在の接続記録
                            ConnectionRecord c = clist.get(i);
                            ...
                            try {
                                // connはIServiceConnectです
                                // IServiceConnect#connectedメソッドを分析します
                                c.conn.connected(r.name, service, false);
                            } /* 省略 catch */
                        }
                    }
                }

                ...
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }


    }

サービスの起動#

public class TestService extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
}

ContextImpl startService が AMS を呼び出す#

ActivityManager をプロキシクラスとして使用して AMS の startService メソッドにアクセスします。ここで、startService はコールバックを行いませんが、ComponentName を返すことで起動が成功したかどうかを判断できます

// ContextImpl.java

    @Override
    public ComponentName startService(Intent service) {
        ...
        return startServiceCommon(service, false, mUser);
    }

    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {

        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);

            // プロキシクラスを通じてAMS#startServiceを呼び出します
            ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
            
            if (cn != null) {
                // サービスを起動する権限がありません
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    // サービスを起動できません
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                } else if (cn.getPackageName().equals("?")) {
                    // サービスを起動することが許可されていません
                    throw ServiceStartNotAllowedException.newInstance(requireForeground,
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            
            ... 省略部分
            
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

AMS - サービスの起動プロセス#

ActivityManagerService#startService
Intent が FileDescriptors を含んでいるかどうかを確認します。
ActiveServices#startServiceLocked メソッドを呼び出します。

// ActivityManagerService.java

    final ActiveServices mServices;

    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage,
            String callingFeatureId, int userId)
            throws TransactionTooLargeException {

        // Intentをチェック
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        // パッケージをチェック
        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        ... デバッグメッセージ

        synchronized(this) {
            // 呼び出し元のpidとuidを取得します
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();

            final long origId = Binder.clearCallingIdentity();


            ComponentName res;
            try {
                // mServicesの型はActiveServicesです
                // startServiceLockedメソッドを分析します
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

ActiveServices

  1. 現在のデバイスに指定されたサービスがあるかどうかを確認します。PKMS を通じて確認します。retrieveServiceLocked メソッドを確認します。
  2. 指定された ServiceRecord を見つけます。
// ActiveServices.java

    // callerは呼び出し元
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired,
            String callingPackage, @Nullable String callingFeatureId, final int userId,
            boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
            throws TransactionTooLargeException {

        ... 省略部分

        // 1. 現在のデバイスに指定されたサービスがあるかどうかを確認します。retrieveServiceLockedメソッドを確認します
        ServiceLookupResult res =
            retrieveServiceLocked(service, null, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false, false);
        
        // サービスが見つからない場合
        if (res == null) {
            return null;
        }

        ...
        
        ServiceRecord r = res.record;
        
        ... 省略部分
        
        // 2. 見つかったServiceRecordはサービスを記述するために使用されます
        return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
                allowBackgroundActivityStarts, backgroundActivityStartsToken);

    }

ServiceRecord はサービスを記述するために使用され、ActivityRecord がアクティビティを記述するのと似ています。

アプリ側の記述AMS 側の記述
アクティビティActivityRecord
サービスServiceRecord

ActiveServices - PKMS を通じてサービスを探す#

AMS がサービスを探す方法は ServiceLoopupResult で、目的は ServiceRecord を見つけることです
ServiceRecord を見つけるための主な方法は PKMS を通じてです

// ActiveServices.java

    private ServiceLookupResult retrieveServiceLocked(Intent service,
            String instanceName, String resolvedType, String callingPackage,
            int callingPid, int callingUid, int userId,
            boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
            boolean allowInstant) {

        // 各サービスはServiceRecordに変換されます
        ServiceRecord r = null;

        ServiceMap smap = getServiceMapLocked(userId);

        // ComponentNameを組み立てます
        final ComponentName comp;

        if (instanceName == null) {    // 現在渡されているinstanceNameはnullです
            comp = service.getComponent();
        } else {

            // Componentを通じてComponentNameを組み立てます
            final ComponentName realComp = service.getComponent();
            if (realComp == null) {
                throw new IllegalArgumentException("Can't use custom instance name '" + instanceName
                        + "' without explicit component in Intent");
            }
            comp = new ComponentName(realComp.getPackageName(),
                    realComp.getClassName() + ":" + instanceName);
        }
        
        // ComponentNameの結果を判断します
        if (comp != null) {
            r = smap.mServicesByInstanceName.get(comp);
        }
        
        ... 省略部分
            
        if (r == null) {
            try {
                
                ...
                
                // PackageManagerを通じて対応するサービスを見つけます
                ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service,
                    resolvedType, flags, userId, callingUid);
                
                ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;
                if (sInfo == null) {
                    // PKMSが見つからない場合は直接nullを返します
                    Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +
                          ": not found");
                    return null;
                }
                ... 省略部分
                
            }  catch (RemoteException ex) {
                // 同じプロセス内なので、発生することはありません
            } 
        }
        
        
        if (r != null) {
            ... 省略部分
                
            return new ServiceLookupResult(r, null);
        }
        
        return null;

    }


    // キーはcallingUser(int)
    final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();

    private ServiceMap getServiceMapLocked(int callingUser) {
        // キャッシュを取得しようとします
        ServiceMap smap = mServiceMap.get(callingUser);
        if (smap == null) {
            // 新しいService記録オブジェクトを再作成します
            smap = new ServiceMap(mAm.mHandler.getLooper(), callingUser);
            mServiceMap.put(callingUser, smap);
        }
        return smap;
    }

    final class ServiceMap extends Handler {
        final int mUserId;
        // 呼び出されたサービスはすべて記録されます
        final ArrayMap<ComponentName, ServiceRecord> mServicesByInstanceName = new ArrayMap<>();
    
        ... 省略部分
    
    }

mServiceMap はユーザーをキーとして使用し、対応する ServiceMap を保存します。内部には ComponentName と ServiceRecord が保存されており、ユーザーが同じサービスを呼び出した場合、キャッシュを通じてサービスを取得できます。

サービスの起動エントリ - startServiceInnerLocked#

// ActiveServices.java
    final ActivityManagerService mAm;

    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    
        // これもFunctionにLockedキーワードが含まれている理由です
        synchronized (mAm.mProcessStats.mLock) {
            final ServiceState stracker = r.getTracker();
            if (stracker != null) {
                stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
            }
        }
        r.callStart = false;
        
        final int uid = r.appInfo.uid;
        final String packageName = r.name.getPackageName();
        final String serviceName = r.name.getClassName();
        
        ... 省略部分
        
        
        // bringUpServiceLockedを通じてサービスを起動します
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
                false /* whileRestarting */,
                false /* permissionsReviewRequired */,
                false /* packageFrozen */,
                true /* enqueueOomAdj */);
        
        ...
        
        return r.name;
    
    }
関数名機能
bringUpServiceLockedプロセスを判断し、起動する場合は AMS を通じて呼び起こします
bringDownServiceLocked接続されているすべての接続を取得し、サービスを停止します
bringUpServiceLocked はプロセスの状態を判断し、サービスが存在するプロセスが起動していない場合、AMS を通じてターゲットプロセスを起動します
// ActiveServices.java

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
            boolean enqueueOomAdj)
            throws TransactionTooLargeException {

        ... 省略判空、Logメッセージ

        // サービスが隔離されているかどうか
        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;

        // サービスが実行されるプロセスを取得します
        final String procName = r.processName;
        HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
        ProcessRecord app;

        if (!isolated) {
            // 1. AMSを通じてProcessRecordオブジェクトを取得しようとします
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
            
            if (app != null) { // ProcessRecordが存在する場合
                
                final IApplicationThread thread = app.getThread();
                final int pid = app.getPid();
                final UidRecord uidRecord = app.getUidRecord();
                
                
                if (thread != null) {
                    try {
                        app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
                                mAm.mProcessStats);
                        
                        // 2. realStartServiceLockedでサービスを実際に起動します
                        realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
                                enqueueOomAdj);
                        
                        return null;
                    } /* 省略 catch*/
                    
                }
            }
        } else {
            ... 省略部分
        }
        
        
        if (app == null && !permissionsReviewRequired && !packageFrozen) {
            // ターゲットプロセスが存在しない場合、AMS#startProcessLockedを通じて起動します
            if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == null) {
                ... 起動失敗
                
                // サービスを停止します
                bringDownServiceLocked(r, enqueueOomAdj);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }
        
        ... 省略部分
    }

実際にサービスを作成する - realStartServiceLocked#

ProcessRecord 内のスレッド(つまり ActivityThread 内の ApplicationThread オブジェクト)を通じて scheduleCreateService メソッドを呼び出します。

// ActiveServices.java

    private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
            boolean enqueueOomAdj) throws RemoteException {

        // ターゲットプロセスを設定します
        r.setProcess(app, thread, pid, uidRecord);

        ... 省略部分

        // ターゲットプロセス内のすべてのサービスを取得します
        final ProcessServiceRecord psr = app.mServices;

        final boolean newService = psr.startService(r);
        ...

        // AMSを通じてOOM_ADJの数値を調整します
        mAm.enqueueOomAdjTargetLocked(app);
        mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);

        boolean created = false;
        try {

            ...

            final int uid = r.appInfo.uid;
            final String packageName = r.name.getPackageName();
            final String serviceName = r.name.getClassName();

            ...

            // threadはアプリ側のIApplicationThreadです
            thread.scheduleCreateService(r, 
                                         r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.mState.getReportedProcState());

            ...

            created = true;
        } /* 省略 catch、finally */

        ... 省略部分

    }

ActivityThread - アプリ側が scheduleCreateService を処理し、メインハンドラーを通じて CREATE_SERVICE をメインスレッドに送信し、その後 handleCreateService メソッドを呼び出します。

// ActivityThread.java

    private class ApplicationThread extends IApplicationThread.Stub {

        public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {

            updateProcessState(processState, false);

            // 必要なデータをCreateServiceDataクラスにラップします
            CreateServiceData s = new CreateServiceData();
            s.token = token;    // トークンはServiceRecordクラスです
            s.info = info;        // サービスのクラス名などの情報を含みます
            s.compatInfo = compatInfo;

            // Handlerを通じてCREATE_SERVICE情報を送信します
            sendMessage(H.CREATE_SERVICE, s);
        }

    }

    class H extends Handler {

        public static final int CREATE_SERVICE  = 114;

        public void handleMessage(Message msg) {
            switch (msg.what) {

                case CREATE_SERVICE:
                    ...

                    handleCreateService((CreateServiceData)msg.obj);
                    break;
            }
        }
    }


    @UnsupportedAppUsage
    private void handleCreateService(CreateServiceData data) {
        Service service = null;


        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        try {

            Application app = packageInfo.makeApplication(false, mInstrumentation);

            ...

            // LoadedApkを通じてサービスオブジェクトを作成します
			// getAppFactory()=>作成されるのはAppComponentFactoryオブジェクトです
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.token);

            // 対応するContextImplオブジェクトを作成します
            ContextImpl context = ContextImpl.getImpl(service
                    .createServiceBaseContext(this, packageInfo));

            // service#attachメソッドを呼び出します
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());

            // onCreateメソッドを呼び出します
            service.onCreate();

            try {
                // AMSにサービスが正常に作成されたことを通知します
				// data.tokenはServiceRecordです
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }

        } /* 省略 catch */
    }

LoadedApkを通じてサービスオブジェクトを作成します。

// LoadedApk.java
    private AppComponentFactory mAppComponentFactory;

    public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
            CompatibilityInfo compatInfo, ClassLoader baseLoader,
            boolean securityViolation, boolean includeCode, boolean registerPackage) {
        mActivityThread = activityThread;
        // デフォルトのApplicationInfo
        mApplicationInfo = new ApplicationInfo();

        ... 

        // createAppFactoryメソッドを通じてファクトリーを作成します
        mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
    }

    public AppComponentFactory getAppFactory() {
        return mAppComponentFactory;
    }

    private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {

        // カスタムファクトリーがある場合は、リフレクションを通じて作成します
        if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
            try {
                return (AppComponentFactory)
                        cl.loadClass(appInfo.appComponentFactory).newInstance();
            } /* 省略部分 */
        }

        // デフォルトのファクトリー
        return AppComponentFactory.DEFAULT;
    }

デフォルトファクトリー AppComponentFactoryClassLoader#loadClassを通じて指定されたクラスを読み込み、リフレクションを通じて指定されたクラスを作成します。

// AppComponentFactory.java

public class AppComponentFactory {

    ... 省略部分

    public @NonNull Service instantiateService(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        // ClassLoader#loadClassを通じて指定されたクラスを読み込み、そのクラスをリフレクションを通じて作成します
        return (Service) cl.loadClass(className).newInstance();
    }

    // デフォルトのファクトリー
    public static final AppComponentFactory DEFAULT = new AppComponentFactory();
}
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。