国内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()等,根据实际需求定制。
boolean blockService(boolean createIfNeeded,
String callname, int callingUid, ServiceRecord r,
Intent service, int userId) {
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,对无源码的单个耗时广播有效。
+/* 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():
public static boolean blockProvider(ApplicationInfo callInfo,
ApplicationInfo infos, int userId) {
+/* 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()
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()
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()
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():
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
+/* 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():
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处理。
+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():
+/* 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():
+/* 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()之前:
+/* 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;
}