OSDN Git Service

Hook up Adaptive Battery to new flag.
[android-x86/packages-apps-Settings.git] / src / com / android / settings / fuelgauge / batterytip / AnomalyDetectionJobService.java
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.settings.fuelgauge.batterytip;
18
19 import static android.os.StatsDimensionsValue.INT_VALUE_TYPE;
20 import static android.os.StatsDimensionsValue.TUPLE_VALUE_TYPE;
21
22 import android.app.AppOpsManager;
23 import android.app.StatsManager;
24 import android.app.job.JobInfo;
25 import android.app.job.JobParameters;
26 import android.app.job.JobScheduler;
27 import android.app.job.JobService;
28 import android.app.job.JobWorkItem;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageManager;
35 import android.os.Bundle;
36 import android.os.Process;
37 import android.os.StatsDimensionsValue;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.provider.Settings;
41 import android.support.annotation.VisibleForTesting;
42 import android.util.Log;
43 import android.util.Pair;
44
45 import com.android.internal.logging.nano.MetricsProto;
46 import com.android.internal.os.BatteryStatsHelper;
47 import com.android.internal.util.ArrayUtils;
48 import com.android.settings.R;
49 import com.android.settings.fuelgauge.BatteryUtils;
50 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
51 import com.android.settings.overlay.FeatureFactory;
52 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
53 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
54 import com.android.settingslib.utils.ThreadUtils;
55
56 import java.util.ArrayList;
57 import java.util.List;
58 import java.util.concurrent.TimeUnit;
59
60 /** A JobService to store anomaly data to anomaly database */
61 public class AnomalyDetectionJobService extends JobService {
62     private static final String TAG = "AnomalyDetectionService";
63     private static final int ON = 1;
64     @VisibleForTesting
65     static final int UID_NULL = -1;
66     @VisibleForTesting
67     static final int STATSD_UID_FILED = 1;
68
69     @VisibleForTesting
70     static final long MAX_DELAY_MS = TimeUnit.MINUTES.toMillis(30);
71
72     public static void scheduleAnomalyDetection(Context context, Intent intent) {
73         final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
74         final ComponentName component = new ComponentName(context,
75                 AnomalyDetectionJobService.class);
76         final JobInfo.Builder jobBuilder =
77                 new JobInfo.Builder(R.integer.job_anomaly_detection, component)
78                         .setOverrideDeadline(MAX_DELAY_MS);
79
80         if (jobScheduler.enqueue(jobBuilder.build(), new JobWorkItem(intent))
81                 != JobScheduler.RESULT_SUCCESS) {
82             Log.i(TAG, "Anomaly detection job service enqueue failed.");
83         }
84     }
85
86     @Override
87     public boolean onStartJob(JobParameters params) {
88         ThreadUtils.postOnBackgroundThread(() -> {
89             final Context context = AnomalyDetectionJobService.this;
90             final BatteryDatabaseManager batteryDatabaseManager =
91                     BatteryDatabaseManager.getInstance(this);
92             final BatteryTipPolicy policy = new BatteryTipPolicy(this);
93             final BatteryUtils batteryUtils = BatteryUtils.getInstance(this);
94             final ContentResolver contentResolver = getContentResolver();
95             final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(this,
96                     true /* collectBatteryBroadcast */);
97             final UserManager userManager = getSystemService(UserManager.class);
98             final PowerWhitelistBackend powerWhitelistBackend = PowerWhitelistBackend.getInstance();
99             final PowerUsageFeatureProvider powerUsageFeatureProvider = FeatureFactory
100                     .getFactory(this).getPowerUsageFeatureProvider(this);
101             final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory
102                     .getFactory(this).getMetricsFeatureProvider();
103
104             for (JobWorkItem item = params.dequeueWork(); item != null;
105                     item = params.dequeueWork()) {
106                 saveAnomalyToDatabase(context, batteryStatsHelper, userManager,
107                         batteryDatabaseManager, batteryUtils, policy, powerWhitelistBackend,
108                         contentResolver, powerUsageFeatureProvider, metricsFeatureProvider,
109                         item.getIntent().getExtras());
110             }
111             jobFinished(params, false /* wantsReschedule */);
112         });
113
114         return true;
115     }
116
117     @Override
118     public boolean onStopJob(JobParameters jobParameters) {
119         return false;
120     }
121
122     @VisibleForTesting
123     void saveAnomalyToDatabase(Context context, BatteryStatsHelper batteryStatsHelper,
124             UserManager userManager,
125             BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils,
126             BatteryTipPolicy policy, PowerWhitelistBackend powerWhitelistBackend,
127             ContentResolver contentResolver, PowerUsageFeatureProvider powerUsageFeatureProvider,
128             MetricsFeatureProvider metricsFeatureProvider, Bundle bundle) {
129         // The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|}
130         final StatsDimensionsValue intentDimsValue =
131                 bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE);
132         final long timeMs = bundle.getLong(AnomalyDetectionReceiver.KEY_ANOMALY_TIMESTAMP,
133                 System.currentTimeMillis());
134         final ArrayList<String> cookies = bundle.getStringArrayList(
135                 StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES);
136         final AnomalyInfo anomalyInfo = new AnomalyInfo(
137                 !ArrayUtils.isEmpty(cookies) ? cookies.get(0) : "");
138         final PackageManager packageManager = context.getPackageManager();
139         Log.i(TAG, "Extra stats value: " + intentDimsValue.toString());
140
141         try {
142             final int uid = extractUidFromStatsDimensionsValue(intentDimsValue);
143             final boolean autoFeatureOn = powerUsageFeatureProvider.isSmartBatterySupported()
144                     ? Settings.Global.getInt(contentResolver,
145                             Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON) == ON
146                     : Settings.Global.getInt(contentResolver,
147                             Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON) == ON;
148             final String packageName = batteryUtils.getPackageName(uid);
149             final long versionCode = batteryUtils.getAppLongVersionCode(packageName);
150
151             final boolean anomalyDetected;
152             if (isExcessiveBackgroundAnomaly(anomalyInfo)) {
153                 anomalyDetected = batteryUtils.isPreOApp(packageName)
154                         && batteryUtils.isAppHeavilyUsed(batteryStatsHelper, userManager, uid,
155                         policy.excessiveBgDrainPercentage);
156             } else {
157                 anomalyDetected = true;
158             }
159
160             if (anomalyDetected) {
161                 if (batteryUtils.shouldHideAnomaly(powerWhitelistBackend, uid)) {
162                     metricsFeatureProvider.action(context,
163                             MetricsProto.MetricsEvent.ACTION_ANOMALY_IGNORED,
164                             packageName,
165                             Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT,
166                                     anomalyInfo.anomalyType),
167                             Pair.create(MetricsProto.MetricsEvent.FIELD_APP_VERSION_CODE,
168                                     versionCode));
169                 } else {
170                     if (autoFeatureOn && anomalyInfo.autoRestriction) {
171                         // Auto restrict this app
172                         batteryUtils.setForceAppStandby(uid, packageName,
173                                 AppOpsManager.MODE_IGNORED);
174                         databaseManager.insertAnomaly(uid, packageName, anomalyInfo.anomalyType,
175                                 AnomalyDatabaseHelper.State.AUTO_HANDLED,
176                                 timeMs);
177                     } else {
178                         databaseManager.insertAnomaly(uid, packageName, anomalyInfo.anomalyType,
179                                 AnomalyDatabaseHelper.State.NEW,
180                                 timeMs);
181                     }
182                     metricsFeatureProvider.action(context,
183                             MetricsProto.MetricsEvent.ACTION_ANOMALY_TRIGGERED,
184                             packageName,
185                             Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_TYPE,
186                                     anomalyInfo.anomalyType),
187                             Pair.create(MetricsProto.MetricsEvent.FIELD_APP_VERSION_CODE,
188                                     versionCode));
189                 }
190             }
191         } catch (NullPointerException | IndexOutOfBoundsException e) {
192             Log.e(TAG, "Parse stats dimensions value error.", e);
193         }
194     }
195
196     /**
197      * Extract the uid from {@link StatsDimensionsValue}
198      *
199      * The uid dimension has the format: 1:<int> inside the tuple list. Here are some examples:
200      * 1. Excessive bg anomaly: 27:{1:10089|}
201      * 2. Wakeup alarm anomaly: 35:{1:{1:{1:10013|}|}|}
202      * 3. Bluetooth anomaly:    3:{1:{1:{1:10140|}|}|}
203      */
204     @VisibleForTesting
205     int extractUidFromStatsDimensionsValue(StatsDimensionsValue statsDimensionsValue) {
206         if (statsDimensionsValue == null) {
207             return UID_NULL;
208         }
209         if (statsDimensionsValue.isValueType(INT_VALUE_TYPE)
210                 && statsDimensionsValue.getField() == STATSD_UID_FILED) {
211             // Find out the real uid
212             return statsDimensionsValue.getIntValue();
213         }
214         if (statsDimensionsValue.isValueType(TUPLE_VALUE_TYPE)) {
215             final List<StatsDimensionsValue> values = statsDimensionsValue.getTupleValueList();
216             for (int i = 0, size = values.size(); i < size; i++) {
217                 int uid = extractUidFromStatsDimensionsValue(values.get(i));
218                 if (uid != UID_NULL) {
219                     return uid;
220                 }
221             }
222         }
223
224         return UID_NULL;
225     }
226
227     private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) {
228         return anomalyInfo.anomalyType
229                 == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE;
230     }
231 }