persistent保活原理

  persistent 属性的配置 

<application
        android:usesCleartextTraffic="true"
        android:icon="@drawable/ic_launcher"
        android:persistent="true"
        android:label="@string/app_name">

 persistent应用启动时机很早,早于开机广播的发送,以及桌面启动。

frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

public void systemReady(final Runnable goingCallback) 
{
	...
	synchronized (this) {
		// Only start up encryption-aware persistent apps; once user is
		// unlocked we'll come back around and start unaware apps
		startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
		...
		if (skipHome == false) {
			//启动桌面
			startHomeActivityLocked(currentUserId, "systemReady");
		}
		...
		//这里发送完FINISH_BOOTING_MSG后才开始发送开机广播
		postFinishBooting(false, true);
		...
	}
	....
}
void startPersistentApps(int matchFlags) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;

        synchronized (this) {
            try {
                final List<ApplicationInfo> apps = AppGlobals.getPackageManager()
                        .getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
                for (ApplicationInfo app : apps) {
                    if (!"android".equals(app.packageName)) {
                        addAppLocked(app, null, false, null /* ABI override */,
                                ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
                    }
                }
            } catch (RemoteException ex) {
            }
        }
    }

 frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

    private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
        final ArrayList<ApplicationInfo> finalList = new ArrayList<>();

        // reader
        synchronized (mLock) {
            final int numPackages = mPackages.size();
            final int userId = UserHandle.getCallingUserId();
            for (int index = 0; index < numPackages; index++) {
                final AndroidPackage p = mPackages.valueAt(index);

                final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
                        && !p.isDirectBootAware();
                final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
                        && p.isDirectBootAware();

                if (p.isPersistent()
                        && (!mSafeMode || p.isSystem())
                        && (matchesUnaware || matchesAware)) {
                    PackageSetting ps = mSettings.getPackageLPr(p.getPackageName());
                    if (ps != null) {
                        ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
                                ps.readUserState(userId), userId, ps);
                        if (ai != null) {
                            finalList.add(ai);
                        }
                    }
                }
            }
        }

        return finalList;
    }

 在 PMS 中,有一个记录所有的程序包信息的哈希表(mPackages),每个表项中含有 ApplicationInfo 信息,该信息的 flags(int 型)数据中有一个专门的 bit 用于表示 persistent。getPersistentApplications () 函数会遍历这张表,找出所有 persistent 包,并返回 ArrayList<ApplicationInfo>。

     从代码里可以看出,带 persistent 标志的系统应用(即 flags 中设置了 FLAG_SYSTEM)是一定会被选上的,但如果不是系统应用的话,则要进一步判断当前是否处于 “安全模式”,一旦处于安全模式,那么就算应用设置了 persistent 属性,也不会被选中。

persistent保活原理

 persistent 应用会顽固地运行于系统之中,从系统一启动,一直到系统关机。

    为了保证这种持久性,persistent 应用必须能够在异常出现时,自动重新启动。在 Android 里是这样实现的。每个 ActivityThread 中会有一个专门和 AMS 通信的 binder 实体 final ApplicationThread mAppThread。这个实体在 AMS 中对应的代理接口为 IApplicationThread。

    当 AMS 执行到 attachApplicationLocked () 时,会针对目标用户进程的 IApplicationThread 接口,注册一个 binder 讣告监听器,一旦日后用户进程意外挂掉,AMS 就能在第一时间感知到,并采取相应的措施。如果 AMS 发现意外挂掉的应用是 persistent 的,它会尝试重新启动这个应用。

frameworks\base\core\java\android\app\ActivityThread.java

@UnsupportedAppUsage
final ApplicationThread mAppThread = new ApplicationThread();

frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

    private final class AppDeathRecipient implements IBinder.DeathRecipient {
        final ProcessRecord mApp;
        final int mPid;
        final IApplicationThread mAppThread;

        AppDeathRecipient(ProcessRecord app, int pid,
                IApplicationThread thread) {
            if (DEBUG_ALL) Slog.v(
                TAG, "New death recipient " + this
                 + " for thread " + thread.asBinder());
            mApp = app;
            mPid = pid;
            mAppThread = thread;
        }

        @Override
        public void binderDied() {
            if (DEBUG_ALL) Slog.v(
                TAG, "Death received in " + this
                + " for thread " + mAppThread.asBinder());
            synchronized(ActivityManagerService.this) {
                appDiedLocked(mApp, mPid, mAppThread, true, null);
            }
        }
    }

 在attachApplicationLocked方法中 注册 binder 讣告监听器,当其监听的 binder 实体死亡时,系统会回调 AppDeathRecipient 的 binderDied ()。

// Tell the process all about itself.

        if (DEBUG_ALL) Slog.v(
                TAG, "Binding process pid " + pid + " to record " + app);

        final String processName = app.processName;
        try {
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.setDeathRecipient(adr);
        } catch (RemoteException e) {
            app.resetPackageList(mProcessStats);
            mProcessList.startProcessLocked(app,
                    new HostingRecord("link fail", processName),
                    ZYGOTE_POLICY_FLAG_EMPTY);
            return false;
        }

调用关系如下:

binderDied--》appDiedLocked--》handleAppDiedLocked--》cleanUpApplicationRecordLocked--》startProcessLocked

 一般情况下,当一个应用进程挂掉后,AMS 当然会清理掉其对应的 ProcessRecord,这就是 cleanUpApplicationRecordLocked () 的主要工作。对于 persistent 应用,cleanUpApplicationRecordLocked () 会尝试再次启动对应的应用进程。

    /**
     * Main code for cleaning up a process when it has gone away.  This is
     * called both as a result of the process dying, or directly when stopping
     * a process when running in single process mode.
     *
     * @return Returns true if the given process has been restarted, so the
     * app that was passed in must remain on the process lists.
     */
    @GuardedBy("this")
    final boolean cleanUpApplicationRecordLocked(ProcessRecord app, int pid,
            boolean restarting, boolean allowRestart, int index, boolean replacingPid,
            boolean fromBinderDied) {
        boolean restart;
        synchronized (mProcLock) {
            if (index >= 0) {
                removeLruProcessLocked(app);
                ProcessList.remove(pid);
            }

            // We don't want to unlinkDeathRecipient immediately, if it's not called from binder
            // and it's not isolated, as we'd need the signal to bookkeeping the dying process list.
            restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart,
                    fromBinderDied || app.isolated /* unlinkDeath */);

            // Cancel pending frozen task if there is any.
            mOomAdjuster.mCachedAppOptimizer.unscheduleFreezeAppLSP(app);
        }
        mAppProfiler.onCleanupApplicationRecordLocked(app);
        skipCurrentReceiverLocked(app);
        updateProcessForegroundLocked(app, false, 0, false);
        mServices.killServicesLocked(app, allowRestart);
        mPhantomProcessList.onAppDied(pid);

        // If the app is undergoing backup, tell the backup manager about it
        final BackupRecord backupTarget = mBackupTargets.get(app.userId);
        if (backupTarget != null && pid == backupTarget.app.getPid()) {
            if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
                    + backupTarget.appInfo + " died during backup");
            mHandler.post(new Runnable() {
                @Override
                public void run(){
                    try {
                        IBackupManager bm = IBackupManager.Stub.asInterface(
                                ServiceManager.getService(Context.BACKUP_SERVICE));
                        bm.agentDisconnectedForUser(app.userId, app.info.packageName);
                    } catch (RemoteException e) {
                        // can't happen; backup manager is local
                    }
                }
            });
        }

        mProcessList.scheduleDispatchProcessDiedLocked(pid, app.info.uid);

        // If this is a preceding instance of another process instance
        allowRestart = mProcessList.handlePrecedingAppDiedLocked(app);

        // If the caller is restarting this app, then leave it in its
        // current lists and let the caller take care of it.
        if (restarting) {
            return false;
        }

        if (!app.isPersistent() || app.isolated) {
            if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                    "Removing non-persistent process during cleanup: " + app);
            if (!replacingPid) {
                mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
            }
            mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
        } else if (!app.isRemoved()) {
            // This app is persistent, so we need to keep its record around.
            // If it is not already on the pending app list, add it there
            // and start a new process for it.
            if (mPersistentStartingProcesses.indexOf(app) < 0) {
                mPersistentStartingProcesses.add(app);
                restart = true;
            }
        }
        if ((DEBUG_PROCESSES || DEBUG_CLEANUP) && mProcessesOnHold.contains(app)) Slog.v(
                TAG_CLEANUP, "Clean-up removing on hold: " + app);
        mProcessesOnHold.remove(app);

        mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
        mProcessList.noteProcessDiedLocked(app);

        if (restart && allowRestart && !app.isolated) {
            // We have components that still need to be running in the
            // process, so re-launch it.
            if (index < 0) {
                ProcessList.remove(pid);
            }

            // Remove provider publish timeout because we will start a new timeout when the
            // restarted process is attaching (if the process contains launching providers).
            mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app);

            mProcessList.addProcessNameLocked(app);
            app.setPendingStart(false);
            mProcessList.startProcessLocked(app, new HostingRecord("restart", app.processName),
                    ZYGOTE_POLICY_FLAG_EMPTY);
            return true;
        } else if (pid > 0 && pid != MY_PID) {
            // Goodbye!
            removePidLocked(pid, app);
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
            if (app.isolated) {
                mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
            }
            app.setPid(0);
        }
        return false;
    }

 扩展

在 AMS 中,有一个 isAllowedWhileBooting () 函数 ,persistent 属性设为 true 的应用,允许在 boot 的过程中启动的。

boolean isAllowedWhileBooting(ApplicationInfo ai) {
        return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
    } 

frameworks\base\services\core\java\com\android\server\am\ProcessList.java 

startProcessLocked方法

// If the system is not ready yet, then hold off on starting this
        // process until it is.
        if (!mService.mProcessesReady
                && !mService.isAllowedWhileBooting(info)
                && !allowWhileBooting) {
            if (!mService.mProcessesOnHold.contains(app)) {
                mService.mProcessesOnHold.add(app);
            }
            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
                    "System not ready, putting on hold: " + app);
            checkSlow(startTime, "startProcess: returning with proc on hold");
            return app;
        }

        checkSlow(startTime, "startProcess: stepping in to startProcess");
        final boolean success =
                startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
        checkSlow(startTime, "startProcess: done starting proc!");
        return success ? app : null;

当系统已经处于以下几种情况时,多参数的 startProcessLocked () 会进一步调用另一个只有三个参数的 startProcessLocked ():
1)系统已经处于 ready 状态;
2)想要启动 persistent 应用;
3)参数中明确指定可以在 boot 过程中启动应用。

一般情况下,当 AMS 调用 startProcessLocked () 时,传入的 allowWhileBooting 参数都为 false。比如说,当系统需要启动 “某个 content provider 或者某个 service 或者某个特定 activity” 时,此时传给 startProcessLocked () 的 allowWhileBooting 参数是写死为 false 的。只有一种特殊情况下会在该参数中传入 true,那就是当系统发出的广播 intent 中携带有 Intent.FLAG_RECEIVER_BOOT_UPGRADE 标记时,此时允许在系统未 ready 时,启动接受广播的目标进程。

猜你喜欢

转载自blog.csdn.net/xiaowang_lj/article/details/128310976