diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java index 4d8b3e147880e..d2150b80761f5 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java @@ -182,7 +182,7 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { if (mIrs.isPackageExempted(userId, pkgName)) { return mMinSatiatedBalanceExempted; } - if (mIrs.isHeadlessSystemApp(pkgName)) { + if (mIrs.isHeadlessSystemApp(userId, pkgName)) { return mMinSatiatedBalanceHeadlessSystemApp; } // TODO: take other exemptions into account diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java index dffed0f4a1904..49c6d1b928d74 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.Context; +import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.InstallSourceInfo; @@ -35,9 +36,23 @@ import com.android.internal.util.ArrayUtils; class InstalledPackageInfo { static final int NO_UID = -1; + /** + * Flags to use when querying for front door activities. Disabled components are included + * are included for completeness since the app can enable them at any time. + */ + private static final int HEADLESS_APP_QUERY_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DISABLED_COMPONENTS; + public final int uid; public final String packageName; public final boolean hasCode; + /** + * Whether the app is a system app that is "headless." Headless in this context means that + * the app doesn't have any "front door" activities --- activities that would show in the + * launcher. + */ + public final boolean isHeadlessSystemApp; public final boolean isSystemInstaller; @Nullable public final String installerPackageName; @@ -48,6 +63,17 @@ class InstalledPackageInfo { uid = applicationInfo == null ? NO_UID : applicationInfo.uid; packageName = packageInfo.packageName; hasCode = applicationInfo != null && applicationInfo.hasCode(); + + final PackageManager packageManager = context.getPackageManager(); + final Intent frontDoorActivityIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + .setPackage(packageName); + isHeadlessSystemApp = applicationInfo != null + && (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) + && ArrayUtils.isEmpty( + packageManager.queryIntentActivitiesAsUser( + frontDoorActivityIntent, HEADLESS_APP_QUERY_FLAGS, userId)); + isSystemInstaller = applicationInfo != null && ArrayUtils.indexOf( packageInfo.requestedPermissions, Manifest.permission.INSTALL_PACKAGES) >= 0 @@ -65,4 +91,16 @@ class InstalledPackageInfo { installerPackageName = installSourceInfo == null ? null : installSourceInfo.getInstallingPackageName(); } + + @Override + public String toString() { + return "IPO{" + + "uid=" + uid + + ", pkgName=" + packageName + + (hasCode ? " HAS_CODE" : "") + + (isHeadlessSystemApp ? " HEADLESS_SYSTEM" : "") + + (isSystemInstaller ? " SYSTEM_INSTALLER" : "") + + ", installer=" + installerPackageName + + '}'; + } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java index 1c915bc176043..7f6a75e753977 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -501,12 +501,16 @@ public class InternalResourceService extends SystemService { } } - boolean isHeadlessSystemApp(@NonNull String pkgName) { + boolean isHeadlessSystemApp(final int userId, @NonNull String pkgName) { if (pkgName == null) { Slog.wtfStack(TAG, "isHeadlessSystemApp called with null package"); return false; } synchronized (mLock) { + final InstalledPackageInfo ipo = getInstalledPackageInfo(userId, pkgName); + if (ipo != null && ipo.isHeadlessSystemApp) { + return true; + } // The wellbeing app is pre-set on the device, not expected to be interacted with // much by the user, but can be expected to do work in the background on behalf of // the user. As such, it's a pseudo-headless system app, so treat it as a headless @@ -1754,6 +1758,10 @@ public class InternalResourceService extends SystemService { pw.print("Exempted apps", mExemptedApps); pw.println(); + pw.println(); + pw.print("Wellbeing app="); + pw.println(mWellbeingPackage == null ? "None" : mWellbeingPackage); + boolean printedVips = false; pw.println(); pw.print("VIPs:"); @@ -1832,6 +1840,37 @@ public class InternalResourceService extends SystemService { pw.println(); mAnalyst.dump(pw); + + // Put this at the end since this may be a lot and we want to have the earlier + // information easily accessible. + boolean printedInterestingIpos = false; + pw.println(); + pw.print("Interesting apps:"); + pw.increaseIndent(); + for (int u = 0; u < mPkgCache.numMaps(); ++u) { + for (int p = 0; p < mPkgCache.numElementsForKeyAt(u); ++p) { + final InstalledPackageInfo ipo = mPkgCache.valueAt(u, p); + + // Printing out every single app will be too much. Only print apps that + // have some interesting characteristic. + final boolean isInteresting = ipo.hasCode + && ipo.isHeadlessSystemApp + && !UserHandle.isCore(ipo.uid); + if (!isInteresting) { + continue; + } + + printedInterestingIpos = true; + pw.println(); + pw.print(ipo); + } + } + if (printedInterestingIpos) { + pw.println(); + } else { + pw.print(" None"); + } + pw.decreaseIndent(); } } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java index 526e87683d302..91a291fe20db0 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java @@ -197,7 +197,7 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { final long baseBalance; if (mIrs.isPackageExempted(userId, pkgName)) { baseBalance = mMinSatiatedBalanceExempted; - } else if (mIrs.isHeadlessSystemApp(pkgName)) { + } else if (mIrs.isHeadlessSystemApp(userId, pkgName)) { baseBalance = mMinSatiatedBalanceHeadlessSystemApp; } else { baseBalance = mMinSatiatedBalanceOther; diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java index a9b68eb8f447b..77723d77c1216 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java @@ -150,7 +150,7 @@ public class AlarmManagerEconomicPolicyTest { mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, @@ -184,7 +184,7 @@ public class AlarmManagerEconomicPolicyTest { when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(arcToCake(8), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); } @@ -212,7 +212,7 @@ public class AlarmManagerEconomicPolicyTest { when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java index d66e74a812a74..c5fdb6f1cf2ea 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java @@ -207,7 +207,7 @@ public class CompleteEconomicPolicyTest { when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(arcToCake(10), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); } diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java index 22c731042e428..d41c93ba2ea97 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java @@ -150,7 +150,7 @@ public class JobSchedulerEconomicPolicyTest { mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, @@ -201,7 +201,7 @@ public class JobSchedulerEconomicPolicyTest { when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); assertEquals(arcToCake(6), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(arcToCake(4), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); final String pkgUpdater = "com.pkg.updater"; @@ -235,7 +235,7 @@ public class JobSchedulerEconomicPolicyTest { when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(eq(pkgHeadlessSystemApp))).thenReturn(true); + when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); final String pkgUpdater = "com.pkg.updater";