安卓14系統應用收不到開機廣播 - Wesley’s Blog
前段時間有測試反饋在安卓14 上面某系統應用恢復出廠設置后沒有自啟動,究竟是什么原因呢?
回顧
Android 從3.1開始,會將新安裝并且從未被啟動的應用置為“STOPPED”狀態,或者被force stop的應用,這種狀態下的應用是無法接收到廣播的。但發送方可以通過添加FLAG_INCLUDE_STOPPED_PACKAGES
來豁免,但一般情況下,系統會默認添加FLAG_EXCLUDE_STOPPED_PACKAGES
flag。
http://xrefandroid.com/android-16.0.0_r2/s?refs=FLAG_EXCLUDE_STOPPED_PACKAGES&project=frameworks
/*** If set, this intent will not match any components in packages that* are currently* {@linkplain android.content.pm.ApplicationInfo#FLAG_STOPPED stopped}.* If this is not set, then the default behavior is to include such* applications in the result.*/public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;/*** If set, this intent will always match any components in packages that* are currently* {@linkplain android.content.pm.ApplicationInfo#FLAG_STOPPED stopped}.* This is the default behavior when* {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set. If both of these* flags are set, this one wins (it allows overriding of exclude for* places where the framework may automatically set the exclude flag,* such as broadcasts).*/public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
豁免
IntentResolver.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/IntentResolver.java
private void buildResolveList(@NonNull Computer computer, Intent intent,FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly,String resolvedType, String scheme, F[] src, List<R> dest, int userId,long customFlags) { ...........if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName),userId)) {if (debug) {Slog.v(TAG, " Filter's target is stopped; skipping");}continue;}..................
}
ComponentResolver.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@Overrideprotected boolean isFilterStopped(@Nullable PackageStateInternal packageState,@UserIdInt int userId) {if (!mUserManager.exists(userId)) {return true;}if (packageState == null || packageState.getPkg() == null) {return false;}// System apps are never considered stopped for purposes of// filtering, because there may be no way for the user to// actually re-launch them.return !packageState.isSystem()&& packageState.getUserStateOrDefault(userId).isStopped();}
安卓 14 之前的代碼表明系統應用(系統標記為ApplicationInfo.FLAG_SYSTEM
的應用)是可以豁免的。
注意: 直接安裝在 data 分區的 system uid 應用是不會被標記為ApplicationInfo.FLAG_SYSTEM 的。但更新在 data 分區的系統應用依然有效。
安卓 14 的變化
ComponentResolver.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@Overrideprotected boolean isFilterStopped(@NonNull Computer computer, F filter,@UserIdInt int userId) {if (!mUserManager.exists(userId)) {return true;}final PackageStateInternal packageState = computer.getPackageStateInternal(filter.first.getPackageName());if (packageState == null || packageState.getPkg() == null) {return false;}return packageState.getUserStateOrDefault(userId).isStopped();}
系統應用不能豁免了,一刀切了。🤣 當然,這里可以打補丁讓原來的修改有效。
安卓 15 峰回路轉
ComponentResolver.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@Overrideprotected boolean isFilterStopped(@NonNull Computer computer, F filter,@UserIdInt int userId) {if (!mUserManager.exists(userId)) {return true;}final PackageStateInternal packageState = computer.getPackageStateInternal(filter.first.getPackageName());if (packageState == null || packageState.getPkg() == null) {return false;}if (packageState.isSystem()) {// A system app can be considered in the stopped state only if it was originally// scanned in the stopped state.return packageState.isScannedAsStoppedSystemApp() &&packageState.getUserStateOrDefault(userId).isStopped();}return packageState.getUserStateOrDefault(userId).isStopped();}}
安卓 15 開始,又把系統應用的豁免加回來了,但有了更加精細化的控制。不僅需要滿足是系統應用,同時也要滿足不是被第一次開機標記為停止狀態的系統應用。
isScannedAsStoppedSystemApp()
是用來檢查一個系統應用在系統首次啟動時,是否被自動置于“強制停止”(stopped)狀態。
那么這個狀態是怎么標記的呢?
哪些應用會被標記?
adb可以通過 dumpsys package 包名 | grep isScannedAsStoppedSystemApp
來查看
主要看PackageSetting
的setScannedAsStoppedSystemApp
的調用關系
最后確認為掃描系統 apk 時添加了SCAN_AS_STOPPED_SYSTEM_APP
的掃描標志位的應用會標記為停止狀態。
InstallPackageHelper.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,@ParsingPackageUtils.ParseFlags int parseFlags,@PackageManagerService.ScanFlags int scanFlags,@Nullable UserHandle user) throws PackageManagerException { // A new application appeared on /system, and we are seeing it for the first time.// Its also not updated as we don't have a copy of it on /data. So, scan it in a// STOPPED state.// We'll skip this step under the following conditions:// - It's "android"// - It's an APEX or overlay package since stopped state does not affect them.// - It is enumerated with a <initial-package-state> tag having the stopped attribute// set to false// - It doesn't have an enabled and exported launcher activity, which means the user// wouldn't have a way to un-stop itfinal boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0;if (mPm.mShouldStopSystemPackagesByDefault&& scanSystemPartition&& !pkgAlreadyExists&& !isApexPkg&& !parsedPackage.isOverlayIsStatic()) {String packageName = parsedPackage.getPackageName();if (!"android".contentEquals(packageName)&& !mPm.mInitialNonStoppedSystemPackages.contains(packageName)&& hasLauncherEntry(parsedPackage)) {scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP;}}
}
根據函數的注釋,一個應用會被標記為“停止的系統應用”(Scanned As Stopped System App)需要滿足以下所有條件:
-
是系統應用:即系統標記為
ApplicationInfo.FLAG_SYSTEM
的應用。 -
在core/res/res/values/config.xml 配置config_stopSystemPackagesByDefault為 true,默認啟用。
<!-- Whether system apps should be scanned in the stopped state during initial boot.Packages can be added by OEMs in an allowlist, to prevent them from being scanned as"stopped" during initial boot of a device, or after an OTA update. Stopped state ofan app is not changed during subsequent reboots. --><bool name="config_stopSystemPackagesByDefault">true</bool>
-
應用不存在
-
不是 system_server(包名為 android )
-
不是 APEX 包和靜態資源覆蓋包(Overlay)
-
有啟動入口 (Launcher Entry)。
-
沒有在
initial-package-stopped-states.xml
配置中被豁免。
initial-package-stopped-states.xml - OpenGrok cross reference for /frameworks/base/data/etc/initial-package-stopped-states.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
This XML defines an allowlist for packages that should not be scanned in a "stopped" state.
When this feature is turned on (indicated by the config config_stopSystemPackagesByDefault in
core/res/res/values/config.xml) packages on the system partition that are encountered by
the PackageManagerService for the first time are scanned in the "stopped" state. This allowlist
is also considered while creating new users on the device. Stopped state is not set during
subsequent reboots.Example usage1. <initial-package-state package="com.example.app" stopped="false"/>Indicates that a system package - com.example.app's initial stopped state should not be setby the Package Manager. By default, system apps are marked as stopped.2. <initial-package-state package="com.example.app" stopped="true"/>Indicates that a system package - com.example.app's initial state should be set by thePackage Manager to "stopped=true". It will have the same effect on thepackage's stopped state even if this package was not included in the allow list.3. <initial-package-state package="com.example.app"/>Invalid usage.
--><config></config>
參考
stop應用無法收到廣播問題_應用未啟動接收廣播-CSDN博客
關于Android中App的停止狀態 - 技術小黑屋
PMS 第 4 篇 - PMS_DATA_SCAN_START 階段 | Coolqi`s Blog