--- title: '四大組件 - Service' disqus: kyleAlien --- 四大組件 - Service === ## OverView of Content Service 的基本使用請看 [**Service 使用**](https://hackmd.io/qwwq4oLMTmaw2UksbpPOUg) [TOC] ## Service 概述 * Service 特點 1. Service 也是 **AMS 所管理的對象** 2. **Service 可以單獨啟動,而不用跟隨 Activity 一起啟動** (但同樣要啟動該 Service 所屬的進程,也就是 `ActivityThread`) 3. AMS 會通知 ActivityThread 來啟動 Service * 我們一般 App 啟動 Service 有兩種方式 1. **通過 Context#`startService` 來啟動指定 Service** 2. **通過 Context#`bindService` 綁定 Service** ## 綁定 Service 目前主要分析 App 應用端使用 bindService 方法呼叫指定 Service,連線 Service 時回調 onServiceConnected 接口的狀況 ```java= 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](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ContextImpl.java;l=1?q=ContextImpl.java) - bindService 調用 AMS 註冊 Receiver * **Context 的實作類是 `ContextImpl 類` (原因可以看看 Activity#attach 函數,這裡就不細講),所以在使用 `bindService` 的時候,實作類就是 [ContextImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ContextImpl.java;l=1?q=ContextImpl.java)** 1. **取得 `IServiceConnection` 對象**:透過 LoadApk#getServiceDispatcher 取得 `IServiceConnection` 對象,**IServiceConnection 是 APP 端 IBinder Server 對象**,**將來讓 AMS 做回調用** (下個小節說明) 2. **取得 Binder Proxy 訪問 AMS**:ActivityManager#getService 取得 Binder 代理類,透過代理類呼叫 AMS#`bindIsolatedService` 方法 ```java= // 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(); } } ``` > ![](https://i.imgur.com/ZwEWkkG.png) * 相關 UML 關係 > ![](https://i.imgur.com/tnajC4W.png) ### IServiceConnection 創建 Binder 回調 - Service 連線 * 分析 [**LoadedApk**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/LoadedApk.java;l=1?q=LoadedApk.java)#**getServiceDispatcherCommon 方法**:可以發現它主要在創建 IServiceConnection 接口,該接口之後會讓 AMS 做回調 1. **檢查 Cache**:首先檢查本地緩存是否有該 Service 的 Binder 回調 (Context 作為 Key),如果有的話直接返回,否則創建 ServiceDispatcher 對象 2. 如果沒有就 **創建 LoadedApk.ServiceDispatcher 對象** 3. 透過 getIServiceConnection 取得 Binder Server 回調接口 ```java= // 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` 方法 1. 創建 IServiceConnection.Stub 給 AMS 回調 2. AMS 回調實,會創建 `RunConnection` 對象,讓 `Handler` or `Executor` 呼叫 * App 端 IServiceConnection 服務實作,類關係圖 > ![](https://i.imgur.com/cw04v3h.png) ```java= // 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); } } } } ``` > ![](https://i.imgur.com/LxVEKpn.png) ### AMS 透過 Binder 回調 - ServiceConnection * **RunConnection 類**:是一個 Runnable,以目前狀況來說是使用 Handler,這個 Runnable 會傳到 ActivityThread#H 類 (也是個 Handler) 讓它回調 ```java= // 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.^ 先斷線 `onServiceDisconnected`**,**^2.^ 再次連線 `onServiceConnected`** | | N | IBinder 不為空 | 第一次連線 | 連線呼叫 `onServiceConnected` | ```java= // 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); } } } ``` 下圖是正常回調 > ![](https://i.imgur.com/kXCUVjo.png) ### [**AMS**](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) - 啟動、綁定 Service 過程 :::info 目前進入 AMS 進程,處理 Service 服務綁定 ::: * 客戶端 APP 透過 ContextImpl#bindService,來觸發 AMS#**bindIsolatedService** 方法,綁定 APP & Service 1. 檢查 Service Intent 必須要有 FileDescriptors 2. 呼叫 ActiveServices#bindServiceLocked 方法 ```java= // ActivityManagerService.java // 存活的 Servcie 列表 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); } } ``` :::warning 函數取名中有 Lock 的原因,對呼叫線程堵塞 ::: * [**ActiveServices**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)#bindServiceLocked 方法 1. 檢查呼叫者 進程是否存在 (可能已被 kill),若不存在則拋出 2. 透過 **retrieveServiceLocked 方法**:**去 PKMS 中找到對應的 Service 訊息** :::success * App 端設定的 `BIND_AUTO_CREATE` Flag 若有設置該 Flag,就會透過 `bringUpServiceLocked` 方法 **自動創建該 Service** (之後分析) ::: 3. 透過 **requestServiceBindingLocked 方法**:要求綁定 Service,這個函數包含了第一次綁定 & 重新綁定的功能 (差異在最後一個參數) ```java= // 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 中的 Service ServiceLookupResult res = retrieveServiceLocked(service, instanceName, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant); // 若沒有 res 代表沒有該 Service if (res == null) { return 0; } // 判斷其中的 ServiceRecord if (res.record == null) { return -1; } ServiceRecord s = res.record; ... try { ... // 自動啟動 Service if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); needOomAdj = true; // 自動啟動 Service 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); // Service 連接紀錄 ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent, callerApp.uid, callerApp.processName, callingPackage); ... 調整 OomAdj 數值 // 判斷 Service 進程是否啟動 (app 就是 ProcessReocrd) if (s.app != null && b.intent.received) { try { // 透過使用者傳入的接口進行進行回掉 // 上個小節有分析過 (conn 就是 IServiceConnect),最終會呼叫到 onServiceConnected c.conn.connected(s.name, b.intent.binder, false); } /* 省略 catch */ // 如果當前應用進程是第一個與 Service 進程綁定的 // && 有調用過 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](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java)#requestServiceBindingLocked 方法**:針對傳入的 **ServiceRecord** 來進行判斷、操作 1. 檢查 ServiceRecord 指定的進程是否起動 (檢查 ProcessRecord & IApplicationThread 是否存在),也就是檢查 Service 是否啟動 2. 目前假設 Service 目標進程已經啟動,那就會透過該進程內實現的 IApplicationThread#requestServiceBindingLocked 回到目標進程內 ```java= // 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; } ... Bebug 訊息 // 用戶端沒綁定過 or 呼叫 Service 要重新綁定 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; } ``` :::info * 從這邊可以看出 Service 呼叫 onBind 方法的時機是,尚未連接、重新綁定 ::: > ![](https://i.imgur.com/F5q8qfx.png) ### App 進程 - Service#onBind 呼叫 :::info 目前在使用者呼叫的 Service 進程 ::: * [**ActivityThread**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java) - scheduleBindService 進入 Service 所在進程做綁定 1. 創建 BindServiceData 對象,儲存 `ServiceRecord` & `Intent` 訊息 2. Server 進程收到 Binder 通訊通知後,透過 Handler 對主線程發送 `BIND_SERVICE` 訊息 3. 主線程接收到 `BIND_SERVICE` 消息後,呼叫 handleBindService 方法,如果是第一次綁定就 ^1.^ 呼叫該 Service 的 **onBind** 方法,再接著 ^2.^ 呼叫 AMS#**publishService** 方法 ```java= // 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); // 若取不到,則代表 Service 尚未啟動 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 該 Service 正在運行中 ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_START, data.startId, res); } /* 省略 catch */ } /* 省略 catch */ } } ``` > ![](https://i.imgur.com/Z5HdJP4.png) ### [Service](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) 發布 - 通知所有使用者 * 若 Service 進程啟動第一次啟動,就會呼叫 AMS#publishService 方法 ```java= // 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**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java)#publishServiceLocked 透過 ServiceRecord 取得所有與該 Serice 有連接的 APP 端,並逐一進行通知 :::success * ServiceRecord 會記得所有與該 Service 連接的 APP ::: ```java= // ActiveServices.java // token 就是 BindServiceData void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { final long origId = Binder.clearCallingIdentity(); try { ... debug 訊息 if (r != null) { ... // 取得該 Service 的綁定訊息 IntentBindRecord b = r.bindings.get(filter); if (b != null && !b.received) { b.binder = service; b.requested = true; b.received = true; // 透過 ServiceRecord 取得,所有與 該 Service 連線的紀錄 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); } } ``` ## 啟動 Service 分析啟動 Service 的另外一種方式 Context#startService ```java= public class TestService extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(this, MyService.class); startService(intent); } } ``` ### [ContextImpl](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java) startService 呼叫 AMS * 透過 ActivityManager 作為代理類訪問 AMS 的 startService 方法,這邊可以看出來 startService 並沒有回調,但是 **可以通過返回 ComponentName 判斷是否啟動成功** ```java= // 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) { // 沒有權限去啟動 Service if (cn.getPackageName().equals("!")) { throw new SecurityException( "Not allowed to start service " + service + " without permission " + cn.getClassName()); } else if (cn.getPackageName().equals("!!")) { // 無法啟動 Service throw new SecurityException( "Unable to start service " + service + ": " + cn.getClassName()); } else if (cn.getPackageName().equals("?")) { // 不允許啟動 Service throw ServiceStartNotAllowedException.newInstance(requireForeground, "Not allowed to start service " + service + ": " + cn.getClassName()); } } ... 省略部分 } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } ``` > ![](https://i.imgur.com/QKdtPJ9.png) ### [AMS](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) - 啟動 Service 過程 * ActivityManagerService#startService 1. 檢查 Intent 是否包含 FileDescriptors 2. 呼叫 ActiveServices#startServiceLocked 方法 ```java= // 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**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java) 1. 檢查目前裝置 (同樣是透過 PKMS) 是否有指定 Service,查看 retrieveServiceLocked 方法 2. 找到指定 ServiceRecord ```java= // 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. 檢查目前裝置是否有指定 Service,查看 retrieveServiceLocked 方法 ServiceLookupResult res = retrieveServiceLocked(service, null, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, false, false); // 找不到 Service if (res == null) { return null; } ... ServiceRecord r = res.record; ... 省略部分 // 2. 找到的 ServiceRecord 就是用於描述一個 Service return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg, allowBackgroundActivityStarts, backgroundActivityStartsToken); } ``` * ServiceRecord 用於描述一個 Service,類似於 ActivityRecord 描述一個 Activity | APP 端描述 | AMS 中描述 | | -------- | -------- | | Activity | ActivityRecord | | Service | ServiceRecord | ### [ActiveServices](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java) - 透過 PKMS 找 Service * AMS 尋找 Service 的方法是 `ServiceLoopupResult`,**目的是要找到 ServiceRecord**,而 **找到 ServiceRecord 主要透過 ++PKMS++** ```java= // 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) { // 每個 Service 都會轉為 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 找對對應的 Service 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; // 呼叫過的 Service 都會記錄起來 final ArrayMap<ComponentName, ServiceRecord> mServicesByInstanceName = new ArrayMap<>(); ... 省略部分 } ``` * mServiceMap 主要是透過 User 作為 Key,儲存相對應的 ServiceMap,內部又有儲存 ComponentName & ServiceRecord,當 User 呼叫過相同的 Service 就可以透過它取得 Service 的緩存 ### Service 啟動入口 - [startServiceInnerLocked](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java) ```java= // 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 啟動 Service String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false /* whileRestarting */, false /* permissionsReviewRequired */, false /* packageFrozen */, true /* enqueueOomAdj */); ... return r.name; } ``` | 函數名 | 功能 | | -------- | -------- | | bringUpServiceLocked | 判斷 Process,若設為啟動則透過 AMS 喚醒 | | bringDownServiceLocked | 取得連接該 Service 的所有連線,關閉 Service | :::success * Function 有 Lock 關鍵字 * 在呼叫這個 Function 時,該 Function 會透過 synchronized 來 Locked 呼叫者線程 (必須順序處理該函數) ::: * bringUpServiceLocked 判斷 Process 狀態,若 Service 應在要所在的進程沒有啟動則透過 AMS 啟動目標 Process ```java= // ActiveServices.java private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, boolean enqueueOomAdj) throws TransactionTooLargeException { ... 省略判空、Log 訊息 // 該 Service 是否隔離 final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; // 獲取 Service 要運行在哪個進程 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 真正啟動 Service 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) { ... 啟動失敗 // 關閉 Servcie bringDownServiceLocked(r, enqueueOomAdj); return msg; } if (isolated) { r.isolatedProc = app; } } ... 省略部分 } ``` :::info * 目標 process 不存在 * 透過 AMS#**startProcessLocked** 目標進程啟動 ::: 以下是 Service 進程存在的情況 > ![](https://i.imgur.com/Sy5slYJ.png) ### 真正創建 Service - [realStartServiceLocked](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java) * 透過 ProcessRecord 中的 thread (也就是 AcitivtyThread 內的 ApplicationThread 對象) 呼叫 scheduleCreateService 方法 ```java= // 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**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java) - APP 端處理 scheduleCreateService,會透過 MainHandler 傳送 CREATE_SERVICE 到主線程,之後就會呼叫到 handleCreateService 方法 > ![](https://i.imgur.com/aWP4Fof.png) ```java= // 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; // 透過 Hander 傳送 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 創建 Servcie 對象 // getAppFactory() => 創建的是 AppComponentFactory 對象 service = packageInfo.getAppFactory() .instantiateService(cl, data.info.name, data.intent); // 創建對應的 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,Service 創建成功 // data.token 就是 ServiceRecord ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /* 省略 catch */ } ``` 1. 透過 [**LoadedApk**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/LoadedApk.java) 創建 Service 對象 ```java= // 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; } ``` 2. 預設工廠 [**AppComponentFactory**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/AppComponentFactory.java):透過 ClassLoader#loadClass 讀取指定 Class,並透過反射創建指定類 ```java= // 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(); } ``` > ![](https://i.imgur.com/oeFgJgo.png) ## Appendix & FAQ :::info ::: ###### tags: `Android Framework`