From effeb2a6cce8b085535da3729545b2ce72d1a8c6 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Tue, 16 Jun 2020 10:11:56 -0700 Subject: [PATCH] Cache power whitelisted apps. DeviceIdleController sits higher in the lock hierarchy than AppStandbyController, so ASC shouldn't call out to DIC with its lock held. In order to know if an app is power whitelisted, ASC needs to call out to DIC and cache the result outside of holding the lock. Bug: 158779091 Test: atest CtsBatterySavingTestCases Test: atest CtsJobSchedulerTestCases Test: atest CtsUsageStatsTestCases:UsageStatsTest Test: atest FrameworksServicesTests:AppIdleHistoryTests Test: atest FrameworksServicesTests:AppStandbyControllerTests Change-Id: Iff500f3ab00f6881348926604be7ab07693bdf40 --- .../server/usage/AppStandbyController.java | 84 +++++++++++++++---- .../usage/AppStandbyControllerTests.java | 2 +- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 062108757349d..8b8003d8dbb83 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -82,10 +82,10 @@ import android.os.BatteryStats; import android.os.Build; import android.os.Environment; import android.os.Handler; +import android.os.IDeviceIdleController; import android.os.Looper; import android.os.Message; import android.os.PowerManager; -import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -387,6 +387,7 @@ public class AppStandbyController implements AppStandbyInternal { DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver(); IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING); deviceStates.addAction(BatteryManager.ACTION_DISCHARGING); + deviceStates.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); mContext.registerReceiver(deviceStateReceiver, deviceStates); synchronized (mAppIdleLock) { @@ -442,6 +443,9 @@ public class AppStandbyController implements AppStandbyInternal { mSystemServicesReady = true; + // Offload to handler thread to avoid boot time impact. + mHandler.post(mInjector::updatePowerWhitelistCache); + boolean userFileExists; synchronized (mAppIdleLock) { userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM); @@ -1080,15 +1084,11 @@ public class AppStandbyController implements AppStandbyInternal { return STANDBY_BUCKET_EXEMPTED; } if (mSystemServicesReady) { - try { - // We allow all whitelisted apps, including those that don't want to be whitelisted - // for idle mode, because app idle (aka app standby) is really not as big an issue - // for controlling who participates vs. doze mode. - if (mInjector.isNonIdleWhitelisted(packageName)) { - return STANDBY_BUCKET_EXEMPTED; - } - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); + // We allow all whitelisted apps, including those that don't want to be whitelisted + // for idle mode, because app idle (aka app standby) is really not as big an issue + // for controlling who participates vs. doze mode. + if (mInjector.isNonIdleWhitelisted(packageName)) { + return STANDBY_BUCKET_EXEMPTED; } if (isActiveDeviceAdmin(packageName, userId)) { @@ -1754,7 +1754,7 @@ public class AppStandbyController implements AppStandbyInternal { } } - /** Call on a system update to temporarily reset system app buckets. */ + /** Call on system boot to get the initial set of headless system apps. */ private void loadHeadlessSystemAppCache() { Slog.d(TAG, "Loading headless system app cache. appIdleEnabled=" + mAppIdleEnabled); final List packages = mPackageManager.getInstalledPackagesAsUser( @@ -1807,8 +1807,6 @@ public class AppStandbyController implements AppStandbyInternal { + "): " + mCarrierPrivilegedApps); } - final long now = System.currentTimeMillis(); - pw.println(); pw.println("Settings:"); @@ -1874,6 +1872,8 @@ public class AppStandbyController implements AppStandbyInternal { } pw.println("]"); pw.println(); + + mInjector.dump(pw); } /** @@ -1890,7 +1890,7 @@ public class AppStandbyController implements AppStandbyInternal { private PackageManagerInternal mPackageManagerInternal; private DisplayManager mDisplayManager; private PowerManager mPowerManager; - private PowerWhitelistManager mPowerWhitelistManager; + private IDeviceIdleController mDeviceIdleController; private CrossProfileAppsInternal mCrossProfileAppsInternal; int mBootPhase; /** @@ -1898,6 +1898,11 @@ public class AppStandbyController implements AppStandbyInternal { * automatically placed in the RESTRICTED bucket. */ long mAutoRestrictedBucketDelayMs = ONE_DAY; + /** + * Cached set of apps that are power whitelisted, including those not whitelisted from idle. + */ + @GuardedBy("mPowerWhitelistedApps") + private final ArraySet mPowerWhitelistedApps = new ArraySet<>(); Injector(Context context, Looper looper) { mContext = context; @@ -1914,7 +1919,8 @@ public class AppStandbyController implements AppStandbyInternal { void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { - mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class); + mDeviceIdleController = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); mBatteryStats = IBatteryStats.Stub.asInterface( ServiceManager.getService(BatteryStats.SERVICE_NAME)); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); @@ -1965,8 +1971,34 @@ public class AppStandbyController implements AppStandbyInternal { return mBatteryManager.isCharging(); } - boolean isNonIdleWhitelisted(String packageName) throws RemoteException { - return mPowerWhitelistManager.isWhitelisted(packageName, false); + boolean isNonIdleWhitelisted(String packageName) { + if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) { + return false; + } + synchronized (mPowerWhitelistedApps) { + return mPowerWhitelistedApps.contains(packageName); + } + } + + private void updatePowerWhitelistCache() { + if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) { + return; + } + try { + // Don't call out to DeviceIdleController with the lock held. + final String[] whitelistedPkgs = + mDeviceIdleController.getFullPowerWhitelistExceptIdle(); + synchronized (mPowerWhitelistedApps) { + mPowerWhitelistedApps.clear(); + final int len = whitelistedPkgs.length; + for (int i = 0; i < len; ++i) { + mPowerWhitelistedApps.add(whitelistedPkgs[i]); + } + } + } catch (RemoteException e) { + // Should not happen. + Slog.wtf(TAG, "Failed to get power whitelist", e); + } } boolean isRestrictedBucketEnabled() { @@ -2053,6 +2085,19 @@ public class AppStandbyController implements AppStandbyInternal { } return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId); } + + void dump(PrintWriter pw) { + pw.println("mPowerWhitelistedApps=["); + synchronized (mPowerWhitelistedApps) { + for (int i = mPowerWhitelistedApps.size() - 1; i >= 0; --i) { + pw.print(" "); + pw.print(mPowerWhitelistedApps.valueAt(i)); + pw.println(","); + } + } + pw.println("]"); + pw.println(); + } } class AppStandbyHandler extends Handler { @@ -2138,6 +2183,11 @@ public class AppStandbyController implements AppStandbyInternal { case BatteryManager.ACTION_DISCHARGING: setChargingState(false); break; + case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: + if (mSystemServicesReady) { + mHandler.post(mInjector::updatePowerWhitelistCache); + } + break; } } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 2d45f9ea40c76..7d20da1983714 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -213,7 +213,7 @@ public class AppStandbyControllerTests { } @Override - boolean isNonIdleWhitelisted(String packageName) throws RemoteException { + boolean isNonIdleWhitelisted(String packageName) { return mNonIdleWhitelistApps.contains(packageName); }