在我们开发系统级的App时,很有可能就会用到persistent属性。当在AndroidManifest.xml中将persistent属性设置为true时,那么该App就会具有如下两个特性:
- 在系统刚起来的时候,该App也会被启动起来
- 该App被强制杀掉后,系统会重启该App。这种情况只针对系统内置的App,第三方安装的App不会被重启。
1. persistent属性的定义
persistent属性定义在frameworks/base/core/res/res/values/attrs_manifest.xml中:是一个用于控制应用程序特殊持久模式的标志。通常情况下不应被应用程序使用,它要求系统始终保持应用程序的运行。
<!-- Flag to control special persistent mode of an application. This should not normally be used by applications; it requires that the system keep your application running at all times. --> <attr name="persistent" format="boolean" />
2. persistent属性的使用
persistent用在AndroidManifest.xml的application标签上:默认值为false。
<application android:persistent="true|false"> </application>
3. persistent属性的原理分析 基于Android 12源码
下面我们就从源码的角度来分析persistent属性的工作原理。
3.1 persistent属性的解析
属性的解析主要发生在App安装或者系统启动的时候,解析代码的位置在:/frameworks/base/core/java/android/content/pm/PackageParser.java
深入到PackageParser.java的parseBaseApplication方法中:
if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_persistent, false)) { // Check if persistence is based on a feature being present final String requiredFeature = sa.getNonResourceString(com.android.internal.R.styleable .AndroidManifestApplication_persistentWhenFeatureAvailable); if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) { ai.flags |= ApplicationInfo.FLAG_PERSISTENT; } }
在解析完系统中App的包信息后,会将解析好的信息保存在PMS中的mPackages的map中,ApplicationInfo的flag中有一个bit位用于保存该App是否是persistent。
这里主要是将persistent的flag设置为ApplicationInfo.FLAG_PERSISTENT
3.2 系统启动persistent为true的App
在系统启动时,会启动persistent属性为true的App,代码位置在:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
在系统启动时,AMS中的systemReady()方法会将所有在AndroidManifest中设置了persistent为true的App进程拉起来。
深入到AMS的systemReady()方法中:
synchronized (this) { // Only start up encryption-aware persistent apps; once user is // unlocked we'll come back around and start unaware apps t.traceBegin("startPersistentApps"); startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE); t.traceEnd(); // Start up initial activity. mBooting = true; // Enable home activity for system user, so that the system can always boot. We don't // do this when the system user is not setup since the setup wizard should be the one // to handle home activity in this case. if (UserManager.isSplitSystemUser() && Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0) != 0 || SystemProperties.getBoolean(SYSTEM_USER_HOME_NEEDED, false)) { t.traceBegin("enableHomeActivity"); ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class); try { AppGlobals.getPackageManager().setComponentEnabledSetting(cName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0, UserHandle.USER_SYSTEM); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } t.traceEnd(); } if (bootingSystemUser) { t.traceBegin("startHomeOnAllDisplays"); mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); t.traceEnd(); } t.traceBegin("showSystemReadyErrorDialogs"); mAtmInternal.showSystemReadyErrorDialogsIfNeeded(); t.traceEnd(); if (bootingSystemUser) { t.traceBegin("sendUserStartBroadcast"); final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { Intent intent = new Intent(Intent.ACTION_USER_STARTED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, null, intent, null, new IIntentReceiver.Stub() { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {} }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, null, OP_NONE, null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, UserHandle.USER_ALL); } catch (Throwable e) { Slog.wtf(TAG, "Failed sending first user broadcasts", e); } finally { Binder.restoreCallingIdentity(ident); } t.traceEnd(); } else { Slog.i(TAG, "Not sending multi-user broadcasts for non-system user " + currentUserId); } t.traceBegin("resumeTopActivities"); mAtmInternal.resumeTopActivities(false /* scheduleIdle */); t.traceEnd(); if (bootingSystemUser) { t.traceBegin("sendUserSwitchBroadcasts"); mUserController.sendUserSwitchBroadcasts(-1, currentUserId); t.traceEnd(); } t.traceBegin("setBinderProxies"); BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK, BINDER_PROXY_LOW_WATERMARK); BinderInternal.nSetBinderProxyCountEnabled(true); BinderInternal.setBinderProxyCountCallback( (uid) -> { Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid " + Process.myUid()); BinderProxy.dumpProxyDebugInfo(); if (uid == Process.SYSTEM_UID) { Slog.i(TAG, "Skipping kill (uid is SYSTEM)"); } else { killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), "Too many Binders sent to SYSTEM"); } }, mHandler); t.traceEnd(); // setBinderProxies t.traceEnd(); // ActivityManagerStartApps t.traceEnd(); // PhaseActivityManagerReady }
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) { } } }
@GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, String abiOverride, int zygotePolicyFlags) { return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */, abiOverride, zygotePolicyFlags); } @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) { return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks, false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags); } // TODO: Move to ProcessList? @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, boolean disableTestApiChecks, String abiOverride, int zygotePolicyFlags) { ProcessRecord app; //传进来的isolated是false,所以就会调用getProcessRecordLocked方法,但由于是第一次启动,所以所有的返回都是app = null if (!isolated) { app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName, info.uid); } else { app = null; } if (app == null) { //为新的app创建新的ProcessRecord对象 app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0, new HostingRecord("added application", customProcess != null ? customProcess : info.processName)); updateLruProcessLocked(app, false, null); updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); } // Report usage as process is persistent and being started. mUsageStatsService.reportEvent(info.packageName, UserHandle.getUserId(app.uid), Event.APP_COMPONENT_USED); // This package really, really can not be stopped. try { //因为是开机第一次启动,所以新的App的启动状态就是将要被启动的状态 所以将App的停止状态stoped设置为false AppGlobals.getPackageManager().setPackageStoppedState( info.packageName, false, UserHandle.getUserId(app.uid)); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + info.packageName + ": " + e); } //如果是系统App,且persistent属性为true,则异常死亡后会重启 if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) { app.setPersistent(true); app.mState.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ); } //如果App已启动,则不处理,否则调用startProcessLocked方法启动App //启动App是异步的,因此会将正在启动,但还没启动完成的App添加到mPersistentStartingProcesses列表中,当启动完成后再移除 if (app.getThread() == null && mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); mProcessList.startProcessLocked(app, new HostingRecord("added application", customProcess != null ? customProcess : app.processName), zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride); } return app; }
在App启动完成后,会在ActivityThread中调用ActivityManagerService的attachApplicationLocked()方法,将该App从mPersistentStartingProcesses移除,并注册一个死亡讣告监听器AppDeathRecipient,用于在App异常被杀后的处理工作。
调用AppGlobals.getPackageManager()的getPersistentApplications方法获取所有在AndroidManifest中设置了persistent属性为true的App
调用ActivityManagerServcies的addAppLocked方法去启动App
frameworks\base\core\java\android\app\AppGlobals.java
/** * Return the raw interface to the package manager. * @return The package manager. */ @UnsupportedAppUsage public static IPackageManager getPackageManager() { return ActivityThread.getPackageManager(); }
frameworks\base\core\java\android\content\pm\IPackageManager.aidl
/** * Retrieve all applications that are marked as persistent. * * @return A List<applicationInfo> containing one entry for each persistent * application. */ ParceledListSlice getPersistentApplications(int flags);
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
@Override public @NonNull ParceledListSlice<ApplicationInfo> getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } 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; }
getPersistentApplications方法会遍历mPackages中所有的App,从判断条件中可以看到只有当在解析persistent属性时,ApplicationInfo的flag设置成了FLAG_PERSISTENT,且是系统App;或者是在非安全模式下,才会被选中。
可以看出被选中的情形有两种:
- 系统App,只要ApplicationInfo的flag设置成了FLAG_PERSISTENT
- 第三方安装的App,不仅要ApplicationInfo的flag设置成了FLAG_PERSISTENT,还需要在非安全模式下
// PackageManagerService attributes that are primitives are referenced through the // pms object directly. Primitives are the only attributes so referenced. // Safe mode means we shouldn't match any third-party components protected final PackageManagerService mService; private boolean safeMode() { return mService.mSafeMode; }
深入到ActivityManagerService的attachApplicationLocked()方法中:
@GuardedBy("this") private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { // Find the application record that is being attached... either via // the pid if we are running in multiple processes, or just pull the // next app record if we are emulating process with anonymous threads. ProcessRecord app; long startTime = SystemClock.uptimeMillis(); long bindApplicationTimeMillis; if (pid != MY_PID && pid >= 0) { synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); } if (app != null && (app.getStartUid() != callingUid || app.getStartSeq() != startSeq)) { String processName = null; final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq); if (pending != null) { processName = pending.processName; } final String msg = "attachApplicationLocked process:" + processName + " startSeq:" + startSeq + " pid:" + pid + " belongs to another existing app:" + app.processName + " startSeq:" + app.getStartSeq(); Slog.wtf(TAG, msg); // SafetyNet logging for b/131105245. EventLog.writeEvent(0x534e4554, "131105245", app.getStartUid(), msg); // If there is already an app occupying that pid that hasn't been cleaned up cleanUpApplicationRecordLocked(app, pid, false, false, -1, true /*replacingPid*/, false /* fromBinderDied */); removePidLocked(pid, app); app = null; } } else { app = null; } // It's possible that process called attachApplication before we got a chance to // update the internal state. if (app == null && startSeq > 0) { final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq); if (pending != null && pending.getStartUid() == callingUid && pending.getStartSeq() == startSeq && mProcessList.handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(), startSeq, true)) { app = pending; } } if (app == null) { Slog.w(TAG, "No pending application record for pid " + pid + " (IApplicationThread " + thread + "); dropping process"); EventLogTags.writeAmDropProcess(pid); if (pid > 0 && pid != MY_PID) { killProcessQuiet(pid); //TODO: killProcessGroup(app.info.uid, pid); // We can't log the app kill info for this process since we don't // know who it is, so just skip the logging. } else { try { thread.scheduleExit(); } catch (Exception e) { // Ignore exceptions. } } return false; } // If this application record is still attached to a previous // process, clean it up now. if (app.getThread() != null) { handleAppDiedLocked(app, pid, true, true, false /* fromBinderDied */); } // 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 AppDeathRecipient adr = new AppDeathRecipient( app, pid, thread); thread.asBinder().linkToDeath(adr, 0); app.setDeathRecipient(adr); } catch (RemoteException e) { app.resetPackageList(mProcessStats); //如果注册死亡讣告监听器失败,也会重新启动App进程 mProcessList.startProcessLocked(app, new HostingRecord("link fail", processName), ZYGOTE_POLICY_FLAG_EMPTY); return false; } EventLogTags.writeAmProcBound(app.userId, pid, app.processName); synchronized (mProcLock) { app.mState.setCurAdj(ProcessList.INVALID_ADJ); app.mState.setSetAdj(ProcessList.INVALID_ADJ); app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ); mOomAdjuster.setAttachingSchedGroupLSP(app); app.mState.setForcingToImportant(null); updateProcessForegroundLocked(app, false, 0, false); app.mState.setHasShownUi(false); app.mState.setCached(false); app.setDebugging(false); app.setKilledByAm(false); app.setKilled(false); // We carefully use the same state that PackageManager uses for // filtering, since we use this flag to decide if we need to install // providers when user is unlocked later app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId)); } mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info); List<ProviderInfo> providers = normalMode ? mCpHelper.generateApplicationProvidersLocked(app) : null; if (providers != null && mCpHelper.checkAppInLaunchingProvidersLocked(app)) { Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG); msg.obj = app; mHandler.sendMessageDelayed(msg, ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS); } checkTime(startTime, "attachApplicationLocked: before bindApplication"); if (!normalMode) { Slog.i(TAG, "Launching preboot mode app: " + app); } if (DEBUG_ALL) Slog.v( TAG, "New app record " + app + " thread=" + thread.asBinder() + " pid=" + pid); final BackupRecord backupTarget = mBackupTargets.get(app.userId); try { int testMode = ApplicationThreadConstants.DEBUG_OFF; if (mDebugApp != null && mDebugApp.equals(processName)) { testMode = mWaitForDebugger ? ApplicationThreadConstants.DEBUG_WAIT : ApplicationThreadConstants.DEBUG_ON; app.setDebugging(true); if (mDebugTransient) { mDebugApp = mOrigDebugApp; mWaitForDebugger = mOrigWaitForDebugger; } } boolean enableTrackAllocation = false; synchronized (mProcLock) { if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) { enableTrackAllocation = true; mTrackAllocationApp = null; } } // If the app is being launched for restore or full backup, set it up specially boolean isRestrictedBackupMode = false; if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) { isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID && ((backupTarget.backupMode == BackupRecord.RESTORE) || (backupTarget.backupMode == BackupRecord.RESTORE_FULL) || (backupTarget.backupMode == BackupRecord.BACKUP_FULL)); } final ActiveInstrumentation instr = app.getActiveInstrumentation(); if (instr != null) { notifyPackageUse(instr.mClass.getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION); } ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s", processName, app.getWindowProcessController().getConfiguration()); ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info; app.setCompat(compatibilityInfoForPackage(appInfo)); ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr); // We deprecated Build.SERIAL and it is not accessible to // Instant Apps and target APIs higher than O MR1. Since access to the serial // is now behind a permission we push down the value. final String buildSerial = (!appInfo.isInstantApp() && appInfo.targetSdkVersion < Build.VERSION_CODES.P) ? sTheRealBuildSerial : Build.UNKNOWN; // Figure out whether the app needs to run in autofill compat mode. AutofillOptions autofillOptions = null; if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) { final AutofillManagerInternal afm = LocalServices.getService( AutofillManagerInternal.class); if (afm != null) { autofillOptions = afm.getAutofillOptions( app.info.packageName, app.info.longVersionCode, app.userId); } } ContentCaptureOptions contentCaptureOptions = null; if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) { final ContentCaptureManagerInternal ccm = LocalServices.getService(ContentCaptureManagerInternal.class); if (ccm != null) { contentCaptureOptions = ccm.getOptionsForPackage(app.userId, app.info.packageName); } } SharedMemory serializedSystemFontMap = null; final FontManagerInternal fm = LocalServices.getService(FontManagerInternal.class); if (fm != null) { serializedSystemFontMap = fm.getSerializedSystemFontMap(); } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.elapsedRealtime(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); if (mPlatformCompat != null) { mPlatformCompat.resetReporting(app.info); } final ProviderInfoList providerList = ProviderInfoList.fromList(providers); if (app.getIsolatedEntryPoint() != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. thread.runIsolatedEntryPoint( app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs()); } else if (instr2 != null) { thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, instr2.mUiAutomationConnection, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), new Configuration(app.getWindowProcessController().getConfiguration()), app.getCompat(), getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, app.getDisabledCompatChanges(), serializedSystemFontMap); } else { thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), new Configuration(app.getWindowProcessController().getConfiguration()), app.getCompat(), getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, app.getDisabledCompatChanges(), serializedSystemFontMap); } if (profilerInfo != null) { profilerInfo.closeFd(); profilerInfo = null; } // Make app active after binding application or client may be running requests (e.g // starting activities) before it is ready. synchronized (mProcLock) { app.makeActive(thread, mProcessStats); checkTime(startTime, "attachApplicationLocked: immediately after bindApplication"); } updateLruProcessLocked(app, false, null); checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked"); final long now = SystemClock.uptimeMillis(); synchronized (mAppProfiler.mProfilerLock) { app.mProfile.setLastRequestedGc(now); app.mProfile.setLastLowMemory(now); } } catch (Exception e) { // We need kill the process group here. (b/148588589) Slog.wtf(TAG, "Exception thrown during bind of " + app, e); app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); app.killLocked("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true); handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */); return false; } // Remove this record from the list of starting applications. mPersistentStartingProcesses.remove(app); if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES, "Attach application locked removing on hold: " + app); mProcessesOnHold.remove(app); boolean badApp = false; boolean didSomething = false; // See if the top visible activity is waiting to run in this process... if (normalMode) { try { didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown launching activities in " + app, e); badApp = true; } } // Find any services that should be running in this process... if (!badApp) { try { didSomething |= mServices.attachApplicationLocked(app, processName); checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked"); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown starting services in " + app, e); badApp = true; } } // Check if a next-broadcast receiver is in this process... if (!badApp && isPendingBroadcastProcessLocked(pid)) { try { didSomething |= sendPendingBroadcastsLocked(app); checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked"); } catch (Exception e) { // If the app died trying to launch the receiver we declare it 'bad' Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e); badApp = true; } } // Check whether the next backup agent is in this process... if (!badApp && backupTarget != null && backupTarget.app == app) { if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "New app is backup target, launching agent for " + app); notifyPackageUse(backupTarget.appInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_BACKUP); try { thread.scheduleCreateBackupAgent(backupTarget.appInfo, compatibilityInfoForPackage(backupTarget.appInfo), backupTarget.backupMode, backupTarget.userId, backupTarget.operationType); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e); badApp = true; } } if (badApp) { app.killLocked("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true); handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */); return false; } if (!didSomething) { updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked"); } FrameworkStatsLog.write( FrameworkStatsLog.PROCESS_START_TIME, app.info.uid, pid, app.info.packageName, FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD, app.getStartTime(), (int) (bindApplicationTimeMillis - app.getStartTime()), (int) (SystemClock.elapsedRealtime() - app.getStartTime()), app.getHostingRecord().getType(), (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "")); return true; }
到此,persistent属性为true的App在开机时就会启动,并且会注册死亡讣告监听器AppDeathRecipient。
3.3 系统重新启动被强制kill掉的带有persistent属性的App
上面可知,进程在启动时,会为App注册一个死亡讣告,当App被杀掉后,就会调用AppDeathRecipient的binderDied方法:
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); } } }
binderDied又会调用appDiedLocked()方法:
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread, boolean fromBinderDied) { ...省略... // Clean up already done if the process has been re-started. if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { boolean doLowMem = app.instrumentationClass == null; boolean doOomAdj = doLowMem; ...省略... handleAppDiedLocked(app, false, true); ...省略... }
handleAppDiedLocked又会调用handleAppDiedLocked()方法:
private final void handleAppDiedLocked(ProcessRecord app, boolean restarting, boolean allowRestart) { int pid = app.pid; boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1, false /*replacingPid*/); ...省略... }
继续调用cleanUpApplicationRecordLocked()方法:
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, boolean allowRestart, int index, boolean replacingPid) { ...省略... //非persistent的App被kill后,就会被清理掉 if (!app.persistent || app.isolated) { if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Removing non-persistent process during cleanup: " + app); if (!replacingPid) { removeProcessNameLocked(app.processName, app.uid); } if (mHeavyWeightProcess == app) { mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG, mHeavyWeightProcess.userId, 0)); mHeavyWeightProcess = null; } } else if (!app.removed) { // 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. //该app是persistent的,需要对其进行重启,并把它添加到正在启动的列表中 //设置restart=true if (mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); restart = true; } } ...省略... //通过这个判断添加决定是否重启App进程 //通过前面的过滤,persistent属性的App,restart=true,!app.isolated=true if (restart && !app.isolated) { // We have components that still need to be running in the // process, so re-launch it. if (index < 0) { ProcessList.remove(app.pid); } addProcessNameLocked(app); //启动App进程 startProcessLocked(app, "restart", app.processName); return true; } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! boolean removed; synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); 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; }
到此,带有persistent属性为true的App,就会在强制kill掉进程后,还会重启。
重启persistent应用的调用关系图如下:
persistent应用不建议用于安装,如果已经安装了应用,则需要卸载后再安装,或者push到系统app目录下。
由于persistent应用在启动时 AMS会对系统system应用的 Manifest 进行缓存,因此在push应用后,Manifest 依旧为原来的配置,需要对此目录中对应的应用缓存删除,Android 9的目录为:/data/system/package_cache/1
或者删除原有 /system/priv-app/ 下的应用,再install apk安装,每次安装apk时会生成新的安装应用的缓存。
如果不想删除缓存,也可以修改版本号编译新apk放入系统目录下。