diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java index c2af02a0ec9..619913de1b1 100644 --- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java +++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java @@ -40,8 +40,11 @@ import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.internal.os.BatteryStatsHelper; +import com.android.internal.util.ArrayUtils; import com.android.settings.R; import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.fuelgauge.PowerUsageFeatureProvider; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; import com.android.settingslib.utils.ThreadUtils; @@ -84,11 +87,14 @@ public class AnomalyDetectionJobService extends JobService { true /* collectBatteryBroadcast */); final UserManager userManager = getSystemService(UserManager.class); final PowerWhitelistBackend powerWhitelistBackend = PowerWhitelistBackend.getInstance(); + final PowerUsageFeatureProvider powerUsageFeatureProvider = FeatureFactory + .getFactory(this).getPowerUsageFeatureProvider(this); for (JobWorkItem item = params.dequeueWork(); item != null; item = params.dequeueWork()) { saveAnomalyToDatabase(batteryStatsHelper, userManager, batteryDatabaseManager, batteryUtils, policy, powerWhitelistBackend, contentResolver, + powerUsageFeatureProvider, item.getIntent().getExtras()); } jobFinished(params, false /* wantsReschedule */); @@ -106,42 +112,52 @@ public class AnomalyDetectionJobService extends JobService { void saveAnomalyToDatabase(BatteryStatsHelper batteryStatsHelper, UserManager userManager, BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils, BatteryTipPolicy policy, PowerWhitelistBackend powerWhitelistBackend, - ContentResolver contentResolver, Bundle bundle) { + ContentResolver contentResolver, PowerUsageFeatureProvider powerUsageFeatureProvider, + Bundle bundle) { // The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|} final StatsDimensionsValue intentDimsValue = bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE); - final long subscriptionId = bundle.getLong(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, - -1); final long timeMs = bundle.getLong(AnomalyDetectionReceiver.KEY_ANOMALY_TIMESTAMP, System.currentTimeMillis()); + final String[] cookies = bundle.getStringArray( + StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES); + final AnomalyInfo anomalyInfo = new AnomalyInfo( + !ArrayUtils.isEmpty(cookies) ? cookies[0] : ""); Log.i(TAG, "Extra stats value: " + intentDimsValue.toString()); try { final int uid = extractUidFromStatsDimensionsValue(intentDimsValue); - final int anomalyType = StatsManagerConfig.getAnomalyTypeFromSubscriptionId( - subscriptionId); - final boolean smartBatteryOn = Settings.Global.getInt(contentResolver, - Settings.Global.APP_STANDBY_ENABLED, ON) == ON; + final boolean autoFeatureOn = powerUsageFeatureProvider.isSmartBatterySupported() + ? Settings.Global.getInt(contentResolver, + Settings.Global.APP_STANDBY_ENABLED, ON) == ON + : Settings.Global.getInt(contentResolver, + Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON) == ON; final String packageName = batteryUtils.getPackageName(uid); if (!powerWhitelistBackend.isSysWhitelistedExceptIdle(packageName) && !isSystemUid(uid)) { - if (anomalyType == StatsManagerConfig.AnomalyType.EXCESSIVE_BG) { - // TODO(b/72385333): check battery percentage draining in batterystats - if (batteryUtils.isPreOApp(packageName) && batteryUtils.isAppHeavilyUsed( - batteryStatsHelper, userManager, uid, + boolean anomalyDetected = true; + if (anomalyInfo.anomalyType == StatsManagerConfig.AnomalyType.EXCESSIVE_BG) { + if (!batteryUtils.isPreOApp(packageName) + || !batteryUtils.isAppHeavilyUsed(batteryStatsHelper, userManager, uid, policy.excessiveBgDrainPercentage)) { - Log.e(TAG, "Excessive detected uid=" + uid); + // Don't report if it is not legacy app or haven't used much battery + anomalyDetected = false; + } + } + + if (anomalyDetected) { + if (autoFeatureOn && anomalyInfo.autoRestriction) { + // Auto restrict this app batteryUtils.setForceAppStandby(uid, packageName, AppOpsManager.MODE_IGNORED); - databaseManager.insertAnomaly(uid, packageName, anomalyType, - smartBatteryOn - ? AnomalyDatabaseHelper.State.AUTO_HANDLED - : AnomalyDatabaseHelper.State.NEW, + databaseManager.insertAnomaly(uid, packageName, anomalyInfo.anomalyType, + AnomalyDatabaseHelper.State.AUTO_HANDLED, + timeMs); + } else { + databaseManager.insertAnomaly(uid, packageName, anomalyInfo.anomalyType, + AnomalyDatabaseHelper.State.NEW, timeMs); } - } else { - databaseManager.insertAnomaly(uid, packageName, anomalyType, - AnomalyDatabaseHelper.State.NEW, timeMs); } } } catch (NullPointerException | IndexOutOfBoundsException e) { diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyInfo.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyInfo.java new file mode 100644 index 00000000000..a4077be9d59 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyInfo.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge.batterytip; + +import android.util.KeyValueListParser; + +/** + * Model class to parse and store anomaly info from westworld + */ +public class AnomalyInfo { + private static final String KEY_ANOMALY_TYPE = "anomaly_type"; + private static final String KEY_AUTO_RESTRICTION = "auto_restriction"; + public final Integer anomalyType; + public final boolean autoRestriction; + + public AnomalyInfo(String info) { + KeyValueListParser parser = new KeyValueListParser(','); + parser.setString(info); + anomalyType = parser.getInt(KEY_ANOMALY_TYPE, -1); + autoRestriction = parser.getBoolean(KEY_AUTO_RESTRICTION, false); + } + +} \ No newline at end of file diff --git a/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java b/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java index 62eb7eebbbb..4ae3f102990 100644 --- a/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java +++ b/src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java @@ -41,22 +41,6 @@ public class StatsManagerConfig { */ public static final long SUBSCRIBER_ID = 1; - private static final Map ANOMALY_TYPE; - - private static final HashFunction HASH_FUNCTION = Hashing.sha256(); - - static { - ANOMALY_TYPE = new HashMap<>(); - ANOMALY_TYPE.put(hash("SUBSCRIPTION:SETTINGS_EXCESSIVE_BACKGROUND_SERVICE"), - AnomalyType.EXCESSIVE_BG); - ANOMALY_TYPE.put(hash("SUBSCRIPTION:SETTINGS_LONG_UNOPTIMIZED_BLE_SCAN"), - AnomalyType.BLUETOOTH_SCAN); - ANOMALY_TYPE.put(hash("SUBSCRIPTION:SETTINGS_EXCESSIVE_WAKEUPS_IN_BACKGROUND"), - AnomalyType.WAKEUP_ALARM); - ANOMALY_TYPE.put(hash("SUBSCRIPTION:SETTINGS_EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF"), - AnomalyType.WAKE_LOCK); - } - @Retention(RetentionPolicy.SOURCE) @IntDef({AnomalyType.NULL, AnomalyType.WAKE_LOCK, @@ -71,11 +55,4 @@ public class StatsManagerConfig { int EXCESSIVE_BG = 3; } - public static int getAnomalyTypeFromSubscriptionId(long subscriptionId) { - return ANOMALY_TYPE.getOrDefault(subscriptionId, AnomalyType.NULL); - } - - private static long hash(CharSequence value) { - return HASH_FUNCTION.hashUnencodedChars(value).asLong(); - } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java index 499ab9dc199..d2b11fdb093 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java @@ -22,6 +22,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -41,6 +42,8 @@ import android.os.UserManager; import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; @@ -60,6 +63,10 @@ import java.util.concurrent.TimeUnit; public class AnomalyDetectionJobServiceTest { private static final int UID = 123; private static final String SYSTEM_PACKAGE = "com.android.system"; + private static final String SUBSCRIBER_COOKIES_AUTO_RESTRICTION = + "anomaly_type=6,auto_restriction=true"; + private static final String SUBSCRIBER_COOKIES_NOT_AUTO_RESTRICTION = + "anomaly_type=6,auto_restriction=false"; @Mock private BatteryStatsHelper mBatteryStatsHelper; @Mock @@ -76,6 +83,7 @@ public class AnomalyDetectionJobServiceTest { private BatteryTipPolicy mPolicy; private Bundle mBundle; private AnomalyDetectionJobService mAnomalyDetectionJobService; + private FakeFeatureFactory mFeatureFactory; private Context mContext; @Before @@ -86,6 +94,7 @@ public class AnomalyDetectionJobServiceTest { mPolicy = new BatteryTipPolicy(mContext); mBundle = new Bundle(); mBundle.putParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, mStatsDimensionsValue); + mFeatureFactory = FakeFeatureFactory.setupForTest(); mAnomalyDetectionJobService = spy(new AnomalyDetectionJobService()); } @@ -112,7 +121,7 @@ public class AnomalyDetectionJobServiceTest { mAnomalyDetectionJobService.saveAnomalyToDatabase(mBatteryStatsHelper, mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy, mPowerWhitelistBackend, - mContext.getContentResolver(), mBundle); + mContext.getContentResolver(), mFeatureFactory.powerUsageFeatureProvider, mBundle); verify(mBatteryDatabaseManager, never()).insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), anyLong()); @@ -125,14 +134,16 @@ public class AnomalyDetectionJobServiceTest { mAnomalyDetectionJobService.saveAnomalyToDatabase(mBatteryStatsHelper, mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy, mPowerWhitelistBackend, - mContext.getContentResolver(), mBundle); + mContext.getContentResolver(), mFeatureFactory.powerUsageFeatureProvider, mBundle); verify(mBatteryDatabaseManager, never()).insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), anyLong()); } @Test - public void testSaveAnomalyToDatabase_normalApp_save() { + public void testSaveAnomalyToDatabase_normalAppWithAutoRestriction_save() { + mBundle.putStringArray(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, + new String[]{SUBSCRIBER_COOKIES_AUTO_RESTRICTION}); doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt()); doReturn(false).when(mPowerWhitelistBackend).isSysWhitelisted(SYSTEM_PACKAGE); doReturn(Process.FIRST_APPLICATION_UID).when( @@ -140,9 +151,27 @@ public class AnomalyDetectionJobServiceTest { mAnomalyDetectionJobService.saveAnomalyToDatabase(mBatteryStatsHelper, mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy, mPowerWhitelistBackend, - mContext.getContentResolver(), mBundle); + mContext.getContentResolver(), mFeatureFactory.powerUsageFeatureProvider, mBundle); - verify(mBatteryDatabaseManager).insertAnomaly(anyInt(), anyString(), anyInt(), anyInt(), - anyLong()); + verify(mBatteryDatabaseManager).insertAnomaly(anyInt(), anyString(), eq(6), + eq(AnomalyDatabaseHelper.State.AUTO_HANDLED), anyLong()); + } + + + @Test + public void testSaveAnomalyToDatabase_normalAppWithoutAutoRestriction_save() { + mBundle.putStringArray(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, + new String[]{SUBSCRIBER_COOKIES_NOT_AUTO_RESTRICTION}); + doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt()); + doReturn(false).when(mPowerWhitelistBackend).isSysWhitelisted(SYSTEM_PACKAGE); + doReturn(Process.FIRST_APPLICATION_UID).when( + mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any()); + + mAnomalyDetectionJobService.saveAnomalyToDatabase(mBatteryStatsHelper, mUserManager, + mBatteryDatabaseManager, mBatteryUtils, mPolicy, mPowerWhitelistBackend, + mContext.getContentResolver(), mFeatureFactory.powerUsageFeatureProvider, mBundle); + + verify(mBatteryDatabaseManager).insertAnomaly(anyInt(), anyString(), eq(6), + eq(AnomalyDatabaseHelper.State.NEW), anyLong()); } }