fwrite

fwrite

好好生活
twitter
github
email

服務

服務概述#

服務特點
服務也是 AMS 所管理的對象
服務可以單獨啟動,而不用跟隨 Activity 一起啟動 (但同樣要啟動該服務所屬的進程,也就是 ActivityThread
AMS 會通知 ActivityThread 來啟動服務
一般 App 啟動服務有兩種方式
通過 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 註冊 Receiver#

Context 的實作類是 ContextImpl類 (原因可以看看 Activity#attach 函數,這裡就不細講),所以在使用 bindService 的時候,實作類就是 ContextImpl
取得 IServiceConnection 對象:透過 LoadApk#getServiceDispatcher 取得 IServiceConnection 對象,IServiceConnection 是 APP 端 IBinder Server 對象將來讓 AMS 做回調 (下個小節說明)
取得 Binder Proxy 訪問 AMSActivityManager#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 做回調
檢查 Cache:首先檢查本地緩存是否有該服務的 Binder 回調(Context 作為 Key),如果有的話直接返回,否則創建 ServiceDispatcher 對象
如果沒有就創建 LoadedApk.ServiceDispatcher 對象
透過 getIServiceConnection 取得 Binder Server 回調接口

// 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 對象,讓 HandlerExecutor 呼叫
App 端 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 方法:對 App 傳入的 ServiceConnection 接口進行回調,並有多個狀況如下

是否同名 Server cache目標 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) {
                // Huh, already have this one.  Oh well!
                return;
            }

            if (service != null) {
                // 開始聯機新 Service
                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) {
                    // This service was dead before we got it...  just
                    // don't do anything with it.
                    mActiveConnections.remove(name);
                    return;
                }
            } else {
                // Service disconnect 從緩存中移除
                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 there is a new viable service, it is now connected.
            if (service != null) {

                // 3. 呼叫 onServiceConnected (回到使用者接口)
                mConnection.onServiceConnected(name, service);
            } else {
                mConnection.onNullBinding(name);
            }
        }
    }

AMS - 啟動、綁定服務過程#

客戶端 APP 透過 ContextImpl#bindService,來觸發 AMS#bindIsolatedService 方法,綁定 APP & 服務
檢查服務 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 中找到對應的服務信息
App 端設定的 BIND_AUTO_CREATE Flag 若有設定該 Flag,就會透過 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 代表沒有該服務
        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) {
            // If service is not currently running, can't yet bind.
            return false;
        }

        ... Debug 訊息


        // 客戶端沒綁定過或呼叫服務要重新綁定
        if ((!i.requested || rebind) && i.apps.size() > 0) {

            try {
                // 通知 IApplicationThread (App 端實線) 的 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;
    }

App 進程 - Service#onBind 呼叫#

ActivityThread - scheduleBindService 進入服務所在進程做綁定
創建 BindServiceData 對象,儲存 ServiceRecord & Intent 信息
Server 進程收到 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;

            ... Debug 訊息

            // 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:
                ... 省略 trace

                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 代表 Server 已經啟動
                        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) {
        // Refuse possible leaked file descriptors
        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 取得所有與該服務有連接的 APP 端,並逐一進行通知

// ActiveServices.java

    // token 就是 BindServiceData
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {

        final long origId = Binder.clearCallingIdentity();
        try {
            ... debug 訊息

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

        // 檢查 Package
        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        ... debug 訊息

        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 描述一個 Activity

APP 端描述AMS 中描述
ActivityActivityRecord
ServiceServiceRecord

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

    }


    // Key 是 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 主要是透過 User 作為 Key,儲存相對應的 ServiceMap,內部又有儲存 ComponentName & ServiceRecord,當 User 呼叫過相同的服務就可以透過它取得服務的緩存

服務啟動入口 - 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判斷 Process,若設為啟動則透過 AMS 喚醒
bringDownServiceLocked取得連接該服務的所有聯機,關閉服務
bringUpServiceLocked 判斷 Process 狀態,若服務應在要所在的進程沒有啟動則透過 AMS 啟動目標 Process
// 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) {
            // 目標 process 不存在就透過 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 中的 thread(也就是 ActivityThread 內的 ApplicationThread 對象)呼叫 scheduleCreateService 方法

// ActiveServices.java

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

        // 設定目標 Process
        r.setProcess(app, thread, pid, uidRecord);

        ... 省略部分

        // 取得目標進程內所有的 Services
        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 是 App 端的 IApplicationThread
            thread.scheduleCreateService(r, 
                                         r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.mState.getReportedProcState());

            ...

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

        ... 省略部分

    }

ActivityThread - APP 端處理 scheduleCreateService,會透過 MainHandler 傳送 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;    // Token 就是 ServiceRecord 類
            s.info = info;        // 包有 Service 的 class name ... 等等信息
            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;
        // Default ApplicationInfo
        mApplicationInfo = new ApplicationInfo();

        ... 

        // 透過 createAppFactory 方法創建 Factory
        mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
    }

    public AppComponentFactory getAppFactory() {
        return mAppComponentFactory;
    }

    private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {

        // 若有自定義 Factory 則透過反射創建
        if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
            try {
                return (AppComponentFactory)
                        cl.loadClass(appInfo.appComponentFactory).newInstance();
            } /* 省略部分 */
        }

        // 預設 Factory
        return AppComponentFactory.DEFAULT;
    }

預設工廠 AppComponentFactory:透過 ClassLoader#loadClass 讀取指定 Class,並透過反射創建指定類

// AppComponentFactory.java

public class AppComponentFactory {

    ... 省略部分

    public @NonNull Service instantiateService(@NonNull ClassLoader cl,
            @NonNull String className, @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        // 透過 ClassLoader#loadClass 讀取指定 Class,並透過反射創建該類
        return (Service) cl.loadClass(className).newInstance();
    }

    // 預設 Factory
    public static final AppComponentFactory DEFAULT = new AppComponentFactory();
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。