国内APP各种保活、拉活,这也是导致安卓手机卡慢的主因,为此各大厂商都有自己防自启机制,规避APP的流氓行为,提升手机流畅性。
防自启基本原理:
拦截四大组件(activity、service、receiver、provider)启动流程,并对其它可能主动拉起APP的AccountManagerService、JobService、SyncManager进行拦截,切断一切可能启动APP的线索。
下面针对各组件的拦截点进行汇总(activity不拦截,用户可感知):
service
启动service有两个接口startService、bindService,二者最终都会调用retrieveServiceLocked()检索service信息,我们选择在此统一拦截service的启动。
startService()-->AMS.startServiceLocked()-->retrieveServiceLocked()
bindService()-->AMS.bindServiceLocked()-->retrieveServiceLocked()
retrieveServiceLocked()先在ServiceMap缓存中查询service,查不到再调用PMS.resolveService()进一步查询。我们在查询到ServiceRecord之后拦截。
ActiveServices.java:retrieveServiceLocked():
在blockService()中我们可以根据callname和ServiceRecord信息进行进一步处理,例如黑名单、isSystemAPP()等,根据实际需求定制。
1 2 3 |
boolean blockService(boolean createIfNeeded, String callname, int callingUid, ServiceRecord r, Intent service, int userId){ |
1 2 3 4 5 6 7 8 9 10 11 |
if (r != null) { + /* service block begin */ + if (blockService(createIfNeeded, callingPackage, callingUid, r, service, userId)) { + Slog.w(TAG, "blocking service: " + service + " (" + callingUid + ", " + callingPid + ")"); + return null; + } + /* service block end */ if (mAm.checkComponentPermission(r.permission, callingPid, callingUid, r.appInfo.uid, r.exported) != PackageManager.PERMISSION_GRANTED) { |
receiver
根据调用流程,我们选择在processNextBroadcast()进行广播拦截,有序队列中的动态广播无需拦截,只拦截静态注册,startProcess之前拦截。
sendBroadcast()-->AMS.broadcastIntent()-->broadcastIntentLocked()-->queue.scheduleBroadcastsLocked()-->mHandler.sendMessage(BROADCAST_INTENT_MSG)-->processNextBroadcast()
BroadcastQueue.java:processNextBroadcast():
这里是粗粒度的skip整个广播,后面会介绍针对receivers中某个接收者进行skip,对无源码的单个耗时广播有效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
+/* receiver block begin */ +if (blockReceiver(info, r)) { + Slog.i(TAG, "Skipping delivery of static ["+ mQueueName + "] " + r); + r.receiver = null; + r.curFilter = null; + r.state = BroadcastRecord.IDLE; + scheduleBroadcastsLocked(); + return; +} +/* receiver block end */ if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == null) { |
provider
根据调用流程,我们选择在AMS的getContentProviderImpl()进行拦截。
ContentResolver.query()-->acquireUnstableProvider()-->mMainThread.acquireProvider()-->ActivityManagerNative.getDefault().getContentProvider()-->getContentProviderImpl()
ActivityManagerService.java:getContentProviderImpl():
1 2 |
public static boolean blockProvider(ApplicationInfo callInfo, ApplicationInfo infos, int userId){ |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
+/* provider block begin */ +boolean stoped = + (cpi.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0; +if (stoped && r != null && + blockProvider(r.info, cpi.applicationInfo, userId)) { + return null; +} +/* provider block end */ proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false); |
AccountManagerService、JobService、SyncManager
这几个service不再细述流程,直接贴拦截代码。
- AccountManagerService.java:addAccount()、addAccountAsUser()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); + /* blocking Account begin */ + if(blockBindAccount(mContext, mAuthenticatorCache, + options, accountType, accounts.userId)) return; + /* blocking Account end */ logRecordWithUid( accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId); new Session(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */, null /* accountName */, false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) { |
- JobServiceContext.java:executeRunnableJob()
1 2 3 4 5 6 7 8 9 10 11 12 |
boolean executeRunnableJob(JobStatus job){ synchronized (mLock) { if (!mAvailable) { Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); return false; } + /* blocking job begin */ + if (blockJob(job)) { + return false; + } + /* blocking job end */ mPreferredUid = NO_PREFERRED_UID; |
- SyncManager.java:bindToSyncAdapter()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
boolean bindToSyncAdapter(ComponentName serviceComponent, int userId){ if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this); } Intent intent = new Intent(); intent.setAction("android.content.SyncAdapter"); intent.setComponent(serviceComponent); intent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.sync_binding_label); intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0, null, new UserHandle(userId))); + /* blocking Sync begin */ + if (blockSync(serviceComponent.getPackageName(), userId)) { + return false; + } + /* blocking Sync end */ mBound = true; final boolean bindResult = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mSyncOperation.target.userId)); |
>
补充:receiver耗时与skip
广播超时场景下,我们需要分析出receivers中具体哪个receiver耗时较长,进行优化处理。
对于无源码的APK,广播耗时又比较久,我们也可以针对性的skip该receiver。
动态广播:deliverToRegisteredReceiverLocked()处理;
静态广播:进程已创建,processCurBroadcastLocked()处理;
静态广播:进程未创建,startProcessLocked()创建进程。
耗时分析
原生代码对广播耗时分析不太友好,需要手动比较日志中"BroadcastQueue:Delivering to xxx"前后的时间间隔来判断耗时,繁琐而且不精确。我们采用自行添加log的方式协助日常分析。
无序广播:binder异步分发,需要自行添加log确认耗时时长。
有序广播:每个BroadcastRecord全部处理完毕,会有finish日志,单个receiver的耗时需要自行添加log。
无序广播:
deliverToRegisteredReceiverLocked()-->performReceiveLocked()-->AT.scheduleRegisteredReceiver()-->InnerReceiver.performReceive()-->ReceiverDispatcher.performReceive()-->ReceiverDispatcher.Args.getRunnable()-->onReceive()
LoadedApk.java:ReceiverDispatcher.Args.getRunnable():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
try { ClassLoader cl = mReceiver.getClass().getClassLoader(); intent.setExtrasClassLoader(cl); intent.prepareToEnterProcess(); setExtrasClassLoader(cl); receiver.setPendingResult(this); + long beginTime = SystemClock.uptimeMillis(); receiver.onReceive(mContext, intent); + long endTime = SystemClock.uptimeMillis(); + long duration = endTime - beginTime; + if( duration > 100 ) { //自定义时长,例100ms + Slog.w(TAG, "long-running onReceive" + + ": intent=" + intent + + ", ordered=" + ordered + + ", receiver=" + receiver + + ", duration=" + duration + "ms"); + } } |
有序广播:
每个BroadcastRecord中所有receivers处理完,总时长。
BroadcastQueue.java:processNextBroadcast():
BroadcastQueue: Finished with ordered broadcast BroadcastRecord{9bec22c u13 android.intent.action.USER_INITIALIZE} receivers:(5) take 5783ms in [foreground], remains 5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
+/* ordered time begin */ + long rightNow = SystemClock.uptimeMillis(); + long countTime = rightNow - r.dispatchTime; + Slog.d(TAG_BROADCAST, + "Finished with ordered broadcast " + r + " receivers:(" + + numReceivers + ") take " + countTime + "ms in [" + + mQueueName + "], remains " + + (mOrderedBroadcasts.size() - 1)); +/* ordered time end */ addBroadcastToHistoryLocked(r); if (r.intent.getComponent() == null && r.intent.getPackage() == null && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage, r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime); } mOrderedBroadcasts.remove(0); r = null; looped = true; continue; } } while (r == null); |
BroadcastRecord中单个receiver耗时。
processCurBroadcastLocked()-->AT.scheduleReceiver()-->handleReceiver()-->onReceive()
ActivityThread.java:handleReceiver():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
try { sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); + long beginTime = SystemClock.uptimeMillis(); receiver.onReceive(context.getReceiverRestrictedContext(), data.intent); + long endTime = SystemClock.uptimeMillis(); + long duration = endTime - beginTime; + if( duration > 100 ) { + Slog.w(TAG, "long-running onReceive, static," + + ": intent=" + data.intent + + ", receiver=" + receiver + + ", duration=" + duration + "ms"); + } } |
耗时广播skip
例如发现GMS某个receiver耗时较长,对实际功能又无影响,可skip此receiver。
在deliverToRegisteredReceiverLocked()、processCurBroadcastLocked()、startProcessLocked()三处进行skip处理。
1 2 3 4 |
+static final String[] skipComponents = { + "com.google.android.gms/com.google.android.gms.chimera.GmsIntentOperationService$PersistentTrustedReceiver", + "com.google.android.setupwizard/com.google.android.setupwizard.util.SetupWizardUserInitReceiver", +}; |
deliverToRegisteredReceiverLocked():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
+/* skip componet begin */ +if (skipComponents.contains(r.curComponent)) { + skip = true; +} +/* skip componet end */ if (skip) { r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED; return; } ........ try { if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST, "Delivering to " + filter + " : " + r); if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) { // Skip delivery if full backup in progress // If it's an ordered broadcast, we need to continue to the next receiver. if (ordered) { skipReceiverLocked(r); } } else { performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId); } if (ordered) { r.state = BroadcastRecord.CALL_DONE_RECEIVE; } } |
processCurBroadcastLocked():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
+/* skip componet begin */ +if (skipComponents.contains(r.curComponent)) { + skipReceiverLocked(r); + return; +} +/* skip componet end */ r.receiver = app.thread.asBinder(); r.curApp = app; app.curReceivers.add(r); app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); mService.updateLruProcessLocked(app, false, null); mService.updateOomAdjLocked(); // Tell the application to launch this receiver. r.intent.setComponent(r.curComponent); boolean started = false; try { if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Delivering to component " + r.curComponent + ": " + r); mService.notifyPackageUse(r.intent.getComponent().getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.repProcState); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Process cur broadcast " + r + " DELIVERED for app " + app); started = true; } |
startProcessLocked()之前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
+/* skip componet begin */ +if (skipComponents.contains(r.curComponent)) { + r.receiver = null; + r.curFilter = null; + r.state = BroadcastRecord.IDLE; + scheduleBroadcastsLocked(); + return; +} +/* skip componet end */ if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == null) { return; } |