From 619c5a46d8e20e29c320c35a2b4dea45bca0bc5d Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Wed, 5 May 2021 11:43:06 -0700 Subject: [PATCH] Implement requestForegroundServiceExemption, without enabling it Bug: 187429663 Test: Checked ApplicationInfo.hasRequestForegroundServiceExemption() of a test app, with and without the new attribute set. Change-Id: I98342a35a82e44327db896c9820806eb3eda5b4c --- .../android/os/PowerExemptionManager.java | 6 ++++++ core/api/test-current.txt | 5 +++++ .../android/content/pm/ApplicationInfo.java | 20 +++++++++++++++++++ .../parsing/PackageInfoWithoutStateUtils.java | 4 +++- .../content/pm/parsing/ParsingPackage.java | 2 ++ .../pm/parsing/ParsingPackageImpl.java | 12 +++++++++++ .../pm/parsing/ParsingPackageRead.java | 7 ++++++- .../pm/parsing/ParsingPackageUtils.java | 6 ++++++ core/res/res/values/attrs_manifest.xml | 7 +++++++ core/res/res/values/public.xml | 2 ++ .../com/android/server/am/ActiveServices.java | 8 +++++++- .../server/am/ActivityManagerConstants.java | 20 +++++++++++++++++++ 12 files changed, 96 insertions(+), 3 deletions(-) diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index b62ece6759c60..4a5e529c48db9 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -338,6 +338,9 @@ public class PowerExemptionManager { */ public static final int REASON_MEDIA_SESSION_CALLBACK = 317; + /** @hide The app requests out-out. */ + public static final int REASON_OPT_OUT_REQUESTED = 1000; + /** * The list of BG-FGS-Launch and temp-allow-list reason code. * @hide @@ -404,6 +407,7 @@ public class PowerExemptionManager { REASON_EVENT_MMS, REASON_SHELL, REASON_MEDIA_SESSION_CALLBACK, + REASON_OPT_OUT_REQUESTED, }) @Retention(RetentionPolicy.SOURCE) public @interface ReasonCode {} @@ -700,6 +704,8 @@ public class PowerExemptionManager { return "SHELL"; case REASON_MEDIA_SESSION_CALLBACK: return "MEDIA_SESSION_CALLBACK"; + case REASON_OPT_OUT_REQUESTED: + return "REASON_OPT_OUT_REQUESTED"; default: return "(unknown:" + reasonCode + ")"; } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d366e35dd11a1..9521c2e3a2039 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -50,6 +50,10 @@ package android { field public static final String UNDEFINED = "android.permission-group.UNDEFINED"; } + public static final class R.attr { + field public static final int requestForegroundServiceExemption; + } + public static final class R.bool { field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005 field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004 @@ -806,6 +810,7 @@ package android.content.pm { } public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method public boolean hasRequestForegroundServiceExemption(); method public boolean isPrivilegedApp(); method public boolean isSystemApp(); field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8 diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index a3e0473e39ccf..8b0e992368069 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -778,9 +778,18 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_EXT_PROFILEABLE = 1 << 0; + /** + * Value for {@link #privateFlagsExt}: whether this application has requested + * exemption from the foreground service restriction introduced in S + * (https://developer.android.com/about/versions/12/foreground-services). + * @hide + */ + public static final int PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1 << 1; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = { PRIVATE_FLAG_EXT_PROFILEABLE, + PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlagsExt {} @@ -2444,6 +2453,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) != 0; } + /** + * @return whether the app has requested exemption from the foreground service restrictions. + * This does not take any affect for now. + * @hide + */ + @TestApi + public boolean hasRequestForegroundServiceExemption() { + return (privateFlagsExt + & ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) != 0; + } + /** * @hide */ diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index 980f10d5e688f..75fa1c1b45d72 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -806,7 +806,9 @@ public class PackageInfoWithoutStateUtils { public static int appInfoPrivateFlagsExt(ParsingPackageRead pkg) { // @formatter:off int privateFlagsExt = - flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE); + flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE) + | flag(pkg.hasRequestForegroundServiceExemption(), + ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION); // @formatter:on return privateFlagsExt; } diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index cea50cbaf2341..2413e6d99f5f6 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -338,6 +338,8 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setTheme(int theme); + ParsingPackage setRequestForegroundServiceExemption(boolean requestForegroundServiceExemption); + ParsingPackage setUpgradeKeySets(@NonNull Set upgradeKeySets); ParsingPackage setUse32BitAbi(boolean use32BitAbi); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 711488670f562..b0342aa95fc99 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -464,6 +464,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { CROSS_PROFILE, ENABLED, DISALLOW_PROFILING, + REQUEST_FOREGROUND_SERVICE_EXEMPTION, }) public @interface Values {} private static final long EXTERNAL_STORAGE = 1L; @@ -512,6 +513,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private static final long CROSS_PROFILE = 1L << 43; private static final long ENABLED = 1L << 44; private static final long DISALLOW_PROFILING = 1L << 45; + private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46; } private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) { @@ -2198,6 +2200,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE); } + @Override + public boolean hasRequestForegroundServiceExemption() { + return getBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION); + } + @Override public ParsingPackageImpl setBaseRevisionCode(int value) { baseRevisionCode = value; @@ -2419,6 +2426,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return this; } + @Override + public ParsingPackageImpl setRequestForegroundServiceExemption(boolean value) { + return setBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION, value); + } + @Override public ParsingPackageImpl setUiOptions(int value) { uiOptions = value; diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index 1c2c59f7504c9..35a2b9aeb338b 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -906,10 +906,15 @@ public interface ParsingPackageRead extends Parcelable { */ @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized(); - @Nullable Boolean hasRequestRawExternalStorageAccess(); + /** + * @see ApplicationInfo#hasRequestForegroundServiceExemption() + * @see R.styleable#AndroidManifest_requestForegroundServiceExemption + */ + boolean hasRequestForegroundServiceExemption(); + // TODO(b/135203078): Hide and enforce going through PackageInfoUtils ApplicationInfo toAppInfoWithoutState(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 22d75ef931377..5d74e7452a0b7 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -2033,6 +2033,12 @@ public class ParsingPackageUtils { .AndroidManifestApplication_requestRawExternalStorageAccess, false)); } + if (sa.hasValue( + R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) { + pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable + .AndroidManifestApplication_requestForegroundServiceExemption, + false)); + } } finally { sa.recycle(); } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c5f4fd1a898a7..638627422eff3 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1954,6 +1954,13 @@ See the Changes in backup and restore document for the format of the XML file.--> + + + + diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 1bd53ae247309..aadb25c7ef7ff 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; +import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED; import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN; import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN; import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE; @@ -5874,7 +5875,12 @@ public final class ActiveServices { ret = REASON_OP_ACTIVATE_PLATFORM_VPN; } } - + if (ret == REASON_DENIED) { + if (mAm.mConstants.mFgsAllowOptOut + && targetService.appInfo.hasRequestForegroundServiceExemption()) { + ret = REASON_OPT_OUT_REQUESTED; + } + } return ret; } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 09795855ac27e..0fff8be705741 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -110,6 +110,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate"; static final String KEY_KILL_FAS_CACHED_IDLE = "kill_fas_cached_idle"; + static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; @@ -161,6 +162,7 @@ final class ActivityManagerConstants extends ContentObserver { */ private static final int DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR = 1; + private static final boolean DEFAULT_FGS_ALLOW_OPT_OUT = false; // Flag stored in the DeviceConfig API. /** @@ -493,6 +495,12 @@ final class ActivityManagerConstants extends ContentObserver { */ volatile boolean mKillForceAppStandByAndCachedIdle = DEFAULT_KILL_FAS_CACHED_IDLE; + /** + * Whether to allow "opt-out" from the foreground service restrictions. + * (https://developer.android.com/about/versions/12/foreground-services) + */ + volatile boolean mFgsAllowOptOut = DEFAULT_FGS_ALLOW_OPT_OUT; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -701,6 +709,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_KILL_FAS_CACHED_IDLE: updateKillFasCachedIdle(); break; + case KEY_FGS_ALLOW_OPT_OUT: + updateFgsAllowOptOut(); + break; default: break; } @@ -1040,6 +1051,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_KILL_FAS_CACHED_IDLE); } + private void updateFgsAllowOptOut() { + mFgsAllowOptOut = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_ALLOW_OPT_OUT, + DEFAULT_FGS_ALLOW_OPT_OUT); + } + private void updateImperceptibleKillExemptions() { IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear(); IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages); @@ -1260,6 +1278,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mFgsAtomSampleRate); pw.print(" "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR); pw.print("="); pw.println(mPushMessagingOverQuotaBehavior); + pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT); + pw.print("="); pw.println(mFgsAllowOptOut); pw.println(); if (mOverrideMaxCachedProcesses >= 0) {