安卓14系统应用收不到开机广播

本文最后更新于:2025年8月17日 晚上

前段时间有测试反馈在安卓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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    @Override
protected 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来查看

主要看PackageSettingsetScannedAsStoppedSystemApp 的调用关系

classDiagram
    direction BT
    
    %% 定义接口
    class PackageState {
        <<interface>>
    }
    class PackageStateInternal {
        <<interface>>
    }
    
    %% 定义类
    class SettingBase
    class PackageSetting

    %% 描述关系
    PackageState <|-- PackageStateInternal  
    SettingBase <|-- PackageSetting
    PackageStateInternal <|.. PackageSetting

最后确认为扫描系统 apk 时添加了SCAN_AS_STOPPED_SYSTEM_APP 的扫描标志位的应用会标记为停止状态。

InstallPackageHelper.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 it
final 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)需要满足以下所有条件

  1. 是系统应用:即系统标记为ApplicationInfo.FLAG_SYSTEM 的应用。

  2. 在core/res/res/values/config.xml 配置config_stopSystemPackagesByDefault为 true,默认启用。

    1
    2
    3
    4
    5
    <!-- 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 of
    an app is not changed during subsequent reboots. -->
    <bool name="config_stopSystemPackagesByDefault">true</bool>
  3. 应用不存在

  4. 不是 system_server(包名为 android )

  5. 不是 APEX 包和静态资源覆盖包(Overlay)

  6. 有启动入口 (Launcher Entry)。

  7. 没有在initial-package-stopped-states.xml 配置中被豁免。

initial-package-stopped-states.xml - OpenGrok cross reference for /frameworks/base/data/etc/initial-package-stopped-states.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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 usage
1. <initial-package-state package="com.example.app" stopped="false"/>
Indicates that a system package - com.example.app's initial stopped state should not be set
by 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 the
Package Manager to "stopped=true". It will have the same effect on the
package'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](https://lishuaiqi.top/2018/02/17/PMS4-PMS_DATA_SCAN_START/#1-3-PMS-scanPackageDirtyLI)


安卓14系统应用收不到开机广播
https://iwesley.top/article/3afe9d6d/
作者
Wesley
发布于
2025年8月17日
许可协议