From f1e9e1b25a2e6f872ba339a024d84fa168f18fe9 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Thu, 23 Mar 2023 17:40:21 +0000 Subject: [PATCH] Give headless system apps a higher min balance. Pre-installed apps without a "front door" activity (an activity that is directly from the launcher) won't be interacted with much by the user, but can be expected to do work in the background on behalf of the user. As such, give these apps a higher minimum satiated balance so they can do the work they need to. Bug: 202954395 Test: atest FrameworksMockingServicesTests:AlarmManagerEconomicPolicyTest Test: atest FrameworksMockingServicesTests:CompleteEconomicPolicyTest Test: atest FrameworksMockingServicesTests:JobSchedulerEconomicPolicyTest Change-Id: I79b85c808adcba017de092525d5a0084f7c10834 --- .../tare/AlarmManagerEconomicPolicy.java | 2 +- .../server/tare/InstalledPackageInfo.java | 38 +++++++++++++++++ .../server/tare/InternalResourceService.java | 41 ++++++++++++++++++- .../tare/JobSchedulerEconomicPolicy.java | 2 +- .../tare/AlarmManagerEconomicPolicyTest.java | 6 +-- .../tare/CompleteEconomicPolicyTest.java | 2 +- .../tare/JobSchedulerEconomicPolicyTest.java | 6 +-- 7 files changed, 87 insertions(+), 10 deletions(-) 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";