Android中的persistent属性

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Android中的persistent属性

在我们开发系统级的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&lt;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放入系统目录下。

目录
相关文章
|
6月前
|
Shell API Android开发
android queries属性
android queries属性
495 2
|
6月前
|
XML Java Android开发
Android Studio App开发中改造已有的控件实战(包括自定义支付宝月份选择器、给翻页栏添加新属性、不滚动的列表视图 附源码)
Android Studio App开发中改造已有的控件实战(包括自定义支付宝月份选择器、给翻页栏添加新属性、不滚动的列表视图 附源码)
114 1
|
3月前
|
Android开发
AutoX——当Android中clickable属性显示为false,实际可点击的布局如何处理
AutoX——当Android中clickable属性显示为false,实际可点击的布局如何处理
54 0
|
6月前
|
Java Android开发 C++
Android源代码定制:MK文件执行顺序|属性覆盖
Android源代码定制:MK文件执行顺序|属性覆盖
300 2
Android源代码定制:MK文件执行顺序|属性覆盖
|
6月前
|
Android开发
通过反射方式获取Android设备系统属性
通过反射方式获取Android设备系统属性 【5月更文挑战第1天】
111 2
|
6月前
|
Android开发 C++
Android 系统属性(SystemProperties)
Android 系统属性(SystemProperties)
208 1
|
6月前
|
测试技术 Android开发 开发者
RK3568 Android系统客制化动态替换ro任意属性
RK3568 Android系统客制化动态替换ro任意属性
250 1
|
6月前
|
Android开发
Android android:exported="true" 属性
Android android:exported="true" 属性
878 0
|
6月前
|
Java Android开发
Android开发--Intent-filter属性详解
Android开发--Intent-filter属性详解
45 0
|
6月前
|
Linux Android开发
Android Mediatek 修改UART设备名称和默认调试属性
Android Mediatek 修改UART设备名称和默认调试属性
70 0
下一篇
无影云桌面