ROM定制:APP防自启原理

国内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;
}

你可能感兴趣的:(ROM定制:APP防自启原理)