获取android 13
用户控制: 用户在长时间运行的应用程序上获得更多透明度和控制权:
- 前台服务仍然需要包含通知,并且应用程序必须请求权限才能显示通知。
- FGS 通知现在可以被用户关闭而不影响 FGS
- 用户可以在任务管理器中查看长时间运行的应用列表
- 任务管理器还允许用户停止应用程序
权限定义
权限使用
豁免:与媒体会话有关的通知不受此行为变更的影响。
在ServiceRecord的构造方法中会回调updateFgsHasNotificationPermission方法,去异步(避免死锁)从NMS中去获取此app是否有权限发送通知;ServiceRecord仅记录,具体权限校验在NMS中。
// ServiceRecord.java
// Whether FGS package has permissions to show notifications.
boolean mFgsHasNotificationPermission;
private void updateFgsHasNotificationPermission() {
// Do asynchronous communication with notification manager to avoid deadlocks.
final String localPackageName = packageName;
final int appUid = appInfo.uid;
ams.mHandler.post(new Runnable() {
public void run() {
NotificationManagerInternal nm = LocalServices.getService(
NotificationManagerInternal.class);
if (nm == null) {
return;
}
// Record whether the package has permission to notify the user
mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage(
localPackageName, appUid);
}
});
}
前台任务管理器
无论app的目标sdk版本是多少,android 13上都允许用户从抽屉式通知栏中停止前台服务(整个应用)。停止原因在ApplicationExitInfo表现为REASON_USER_REQUESTED。
以下应用可以运行前台服务,而完全不会显示在任务管理器中:
当以下类型的应用运行前台服务时,它们会显示在 FGS 任务管理器中,但应用名称旁边没有可以供用户按的停止按钮:
// FgsManagerController.kt
fun updateUiControl() {
uiControl = when (activityManager.getBackgroundRestrictionExemptionReason(uid)) {
// 不显示在任务管理器中
PowerExemptionManager.REASON_SYSTEM_UID,
PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
// 显示在任务管理器,但是没有停止按钮
PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE,
PowerExemptionManager.REASON_DEVICE_OWNER,
PowerExemptionManager.REASON_DISALLOW_APPS_CONTROL,
PowerExemptionManager.REASON_DPO_PROTECTED_APP,
PowerExemptionManager.REASON_PROFILE_OWNER,
PowerExemptionManager.REASON_PROC_STATE_PERSISTENT,
PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI,
PowerExemptionManager.REASON_ROLE_DIALER,
PowerExemptionManager.REASON_SYSTEM_MODULE -> UIControl.HIDE_BUTTON
// 正常情况
else -> UIControl.NORMAL
}
uiControlInitialized = true
}
// AppRestrictionController.java
/**
* @return The reason code of whether or not the given UID should be exempted from background
* restrictions here.
*
*
* Note: Call it with caution as it'll try to acquire locks in other services.
*
*/
@ReasonCode
int getBackgroundRestrictionExemptionReason(int uid) {
// uid < 10000
if (UserHandle.isCore(uid)) {
return REASON_SYSTEM_UID;
}
// Whitelist system apps
if (isOnSystemDeviceIdleAllowlist(uid)) {
return REASON_SYSTEM_ALLOW_LISTED;
}
// Whitelist user apps , doze白名单,可联系功耗同学添加
if (isOnDeviceIdleAllowlist(uid)) {
return REASON_ALLOWLISTED_PACKAGE;
}
// 暂无研究
final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
if (am.isAssociatedCompanionApp(UserHandle.getUserId(uid), uid)) {
return REASON_COMPANION_DEVICE_MANAGER;
}
// 处于演示模式的设备上的应用
if (UserManager.isDeviceInDemoMode(mContext) {
return REASON_DEVICE_DEMO_MODE;
}
final int userId = UserHandle.getUserId(uid);
if (mInjector.getUserManagerInternal()
.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)) {
return REASON_DISALLOW_APPS_CONTROL;
}
// 设备所有者
if (am.isDeviceOwner(uid)) {
return REASON_DEVICE_OWNER;
}
// 资料所有者
if (am.isProfileOwner(uid)) {
return REASON_PROFILE_OWNER;
}
// persistent常驻应用
final int uidProcState = am.getUidProcessState(uid);
if (uidProcState <= PROCESS_STATE_PERSISTENT) {
return REASON_PROC_STATE_PERSISTENT;
} else if (uidProcState <= PROCESS_STATE_PERSISTENT_UI) {
return REASON_PROC_STATE_PERSISTENT_UI;
}
final String[] packages = mInjector.getPackageManager().getPackagesForUid(uid);
if (packages != null) {
final AppOpsManager appOpsManager = mInjector.getAppOpsManager();
final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
for (String pkg : packages) {
// VPN
if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN,
uid, pkg) == AppOpsManager.MODE_ALLOWED) {
return REASON_OP_ACTIVATE_VPN;
} else if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN,
uid, pkg) == AppOpsManager.MODE_ALLOWED) {
return REASON_OP_ACTIVATE_PLATFORM_VPN;
} else if (isSystemModule(pkg)) {
return REASON_SYSTEM_MODULE;
} else if (isCarrierApp(pkg)) {
return REASON_CARRIER_PRIVILEGED_APP;
} else if (isExemptedFromSysConfig(pkg)) {
return REASON_SYSTEM_ALLOW_LISTED;
} else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) {
return REASON_SYSTEM_ALLOW_LISTED;
} else if (pm.isPackageStateProtected(pkg, userId)) {
return REASON_DPO_PROTECTED_APP;
} else if (appStandbyInternal.isActiveDeviceAdmin(pkg, userId)) {
return REASON_ACTIVE_DEVICE_ADMIN;
}
}
}
// dialer角色
if (isRoleHeldByUid(RoleManager.ROLE_DIALER, uid)) {
return REASON_ROLE_DIALER;
}
if (isRoleHeldByUid(RoleManager.ROLE_EMERGENCY, uid)) {
return REASON_ROLE_EMERGENCY;
}
return REASON_DENIED;
}
adb shell dumpsys deviceidle // 查看是否在doze白名单,可以不显示停止按钮
adb shell cmd activity stop-app PACKAGE_NAME
长时间运行服务通知
如果系统检测到您的应用长时间运行某项前台服务(在 24 小时的时间段内至少运行 20 小时),便会发送通知邀请用户与前台服务 (FGS) 任务管理器互动。该通知包含以下内容:
APP is running in the background for a long time. Tap to review.
如果前台服务的类型为 FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK 或 FOREGROUND_SERVICE_TYPE_LOCATION,系统将不会显示此通知。
commit 64ac1a923065d38fa33789b315c716f4c93312fc
Author: Jing Ji <jji@google.com>
Date: Sat Dec 11 03:14:45 2021 -0800
Monitor long-running foreground services
If a certain package has foreground services running for a long time,
say the accumulated durations over last X hours are more than Y hours,
system will post a notification to remind the user.
Some type of apps are subjected to be exempted, i.e. if it's already
in the device idle allowlist. More exemption could be added in
the follow-up CLs.
Bug: 200326767
Bug: 203105544
Test: atest FrameworksMockingServicesTests:BackgroundRestrictionTest
Change-Id: I3a8f34c33e7a533240abc7cf4fa569a0956eec73
// 监控滥用(长时间运行)FGS 的跟踪器
frameworks/base/services/core/java/com/android/server/am/AppFGSTracker.java
static final long DEFAULT_BG_FGS_LONG_RUNNING_THRESHOLD = 20 * ONE_HOUR;