diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index afad29c7eebb4..c4795f55fc8a6 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -364,6 +364,11 @@ public class PowerExemptionManager { * @hide */ public static final int REASON_SYSTEM_MODULE = 320; + /** + * Carrier privileged app. + * @hide + */ + public static final int REASON_CARRIER_PRIVILEGED_APP = 321; /** @hide The app requests out-out. */ public static final int REASON_OPT_OUT_REQUESTED = 1000; @@ -440,6 +445,7 @@ public class PowerExemptionManager { REASON_ROLE_DIALER, REASON_ROLE_EMERGENCY, REASON_SYSTEM_MODULE, + REASON_CARRIER_PRIVILEGED_APP, REASON_OPT_OUT_REQUESTED, }) @Retention(RetentionPolicy.SOURCE) @@ -749,6 +755,8 @@ public class PowerExemptionManager { return "ROLE_EMERGENCY"; case REASON_SYSTEM_MODULE: return "SYSTEM_MODULE"; + case REASON_CARRIER_PRIVILEGED_APP: + return "CARRIER_PRIVILEGED_APP"; case REASON_OPT_OUT_REQUESTED: return "REASON_OPT_OUT_REQUESTED"; default: diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 1315293abaa44..152d2781a94e6 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -54,6 +54,7 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE; +import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP; import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; import static android.os.PowerExemptionManager.REASON_DENIED; import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE; @@ -125,6 +126,7 @@ import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.provider.Settings.Global; +import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -229,6 +231,19 @@ public final class AppRestrictionController { @GuardedBy("mLock") private final HashMap mSystemModulesCache = new HashMap<>(); + /** + * Lock specifically for bookkeeping around the carrier-privileged app set. + * Do not acquire any other locks while holding this one. Methods that + * require this lock to be held are named with a "CPL" suffix. + */ + private final Object mCarrierPrivilegedLock = new Object(); + + /** + * List of carrier-privileged apps that should be excluded from standby. + */ + @GuardedBy("mCarrierPrivilegedLock") + private List mCarrierPrivilegedApps; + final ActivityManagerService mActivityManagerService; /** @@ -1604,6 +1619,8 @@ public final class AppRestrictionController { return REASON_OP_ACTIVATE_PLATFORM_VPN; } else if (isSystemModule(pkg)) { return REASON_SYSTEM_MODULE; + } else if (isCarrierApp(pkg)) { + return REASON_CARRIER_PRIVILEGED_APP; } } } @@ -1616,6 +1633,37 @@ public final class AppRestrictionController { return REASON_DENIED; } + private boolean isCarrierApp(String packageName) { + synchronized (mCarrierPrivilegedLock) { + if (mCarrierPrivilegedApps == null) { + fetchCarrierPrivilegedAppsCPL(); + } + if (mCarrierPrivilegedApps != null) { + return mCarrierPrivilegedApps.contains(packageName); + } + return false; + } + } + + private void clearCarrierPrivilegedApps() { + if (DEBUG_BG_RESTRICTION_CONTROLLER) { + Slog.i(TAG, "Clearing carrier privileged apps list"); + } + synchronized (mCarrierPrivilegedLock) { + mCarrierPrivilegedApps = null; // Need to be refetched. + } + } + + @GuardedBy("mCarrierPrivilegedLock") + private void fetchCarrierPrivilegedAppsCPL() { + final TelephonyManager telephonyManager = mInjector.getTelephonyManager(); + mCarrierPrivilegedApps = + telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions(); + if (DEBUG_BG_RESTRICTION_CONTROLLER) { + Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps); + } + } + private boolean isRoleHeldByUid(@NonNull String roleName, int uid) { synchronized (mLock) { final ArrayList roles = mUidRolesMapping.get(uid); @@ -1791,6 +1839,7 @@ public final class AppRestrictionController { private AppBatteryExemptionTracker mAppBatteryExemptionTracker; private AppFGSTracker mAppFGSTracker; private AppMediaSessionTracker mAppMediaSessionTracker; + private TelephonyManager mTelephonyManager; Injector(Context context) { mContext = context; @@ -1890,6 +1939,13 @@ public final class AppRestrictionController { return mRoleManager; } + TelephonyManager getTelephonyManager() { + if (mTelephonyManager == null) { + mTelephonyManager = getContext().getSystemService(TelephonyManager.class); + } + return mTelephonyManager; + } + AppFGSTracker getAppFGSTracker() { return mAppFGSTracker; } @@ -1939,6 +1995,19 @@ public final class AppRestrictionController { onUidAdded(uid); } } + } + // fall through. + case Intent.ACTION_PACKAGE_CHANGED: { + final String pkgName = intent.getData().getSchemeSpecificPart(); + final String[] cmpList = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); + // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package + // enable/disable event (cmpList is just the package name itself), drop + // our carrier privileged app & system-app caches and let them refresh + if (cmpList == null + || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) { + clearCarrierPrivilegedApps(); + } } break; case Intent.ACTION_PACKAGE_FULLY_REMOVED: { final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); @@ -1986,6 +2055,7 @@ public final class AppRestrictionController { }; final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); packageFilter.addDataScheme("package"); mContext.registerReceiverForAllUsers(broadcastReceiver, packageFilter, null, mBgHandler); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 28c91aaefd8ba..76709536b40b6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -101,6 +101,7 @@ import android.os.SystemClock; import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.telephony.TelephonyManager; import android.util.Log; import android.util.Pair; @@ -211,6 +212,7 @@ public final class BackgroundRestrictionTest { @Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal; @Mock private MediaSessionManager mMediaSessionManager; @Mock private RoleManager mRoleManager; + @Mock private TelephonyManager mTelephonyManager; private long mCurrentTimeMillis; @@ -2308,6 +2310,11 @@ public final class BackgroundRestrictionTest { return mRoleManager; } + @Override + TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + @Override AppFGSTracker getAppFGSTracker() { return mAppFGSTracker;