• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.settings.fuelgauge;
17 
18 import android.app.AppOpsManager;
19 import android.content.Context;
20 import android.content.pm.ApplicationInfo;
21 import android.content.pm.PackageManager;
22 import android.os.BatteryStats;
23 import android.os.Bundle;
24 import android.os.Build;
25 import android.os.SystemClock;
26 import android.os.UserManager;
27 import android.support.annotation.IntDef;
28 import android.support.annotation.Nullable;
29 import android.support.annotation.StringRes;
30 import android.support.annotation.VisibleForTesting;
31 import android.text.format.DateUtils;
32 import android.util.Log;
33 import android.util.SparseLongArray;
34 
35 import com.android.internal.os.BatterySipper;
36 import com.android.internal.os.BatteryStatsHelper;
37 import com.android.internal.util.ArrayUtils;
38 import com.android.settings.R;
39 import com.android.settings.fuelgauge.anomaly.Anomaly;
40 import com.android.settings.overlay.FeatureFactory;
41 
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 import java.util.Collections;
45 import java.util.Comparator;
46 import java.util.List;
47 
48 /**
49  * Utils for battery operation
50  */
51 public class BatteryUtils {
52     public static final int UID_NULL = -1;
53     public static final int SDK_NULL = -1;
54 
55     @Retention(RetentionPolicy.SOURCE)
56     @IntDef({StatusType.SCREEN_USAGE,
57             StatusType.FOREGROUND,
58             StatusType.BACKGROUND,
59             StatusType.ALL
60     })
61     public @interface StatusType {
62         int SCREEN_USAGE = 0;
63         int FOREGROUND = 1;
64         int BACKGROUND = 2;
65         int ALL = 3;
66     }
67 
68     private static final String TAG = "BatteryUtils";
69 
70     private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
71     private static final int SECONDS_IN_HOUR = 60 * 60;
72     private static BatteryUtils sInstance;
73 
74     private PackageManager mPackageManager;
75     private AppOpsManager mAppOpsManager;
76     @VisibleForTesting
77     PowerUsageFeatureProvider mPowerUsageFeatureProvider;
78 
getInstance(Context context)79     public static BatteryUtils getInstance(Context context) {
80         if (sInstance == null || sInstance.isDataCorrupted()) {
81             sInstance = new BatteryUtils(context);
82         }
83         return sInstance;
84     }
85 
86     @VisibleForTesting
BatteryUtils(Context context)87     BatteryUtils(Context context) {
88         mPackageManager = context.getPackageManager();
89         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
90         mPowerUsageFeatureProvider = FeatureFactory.getFactory(
91                 context).getPowerUsageFeatureProvider(context);
92     }
93 
getProcessTimeMs(@tatusType int type, @Nullable BatteryStats.Uid uid, int which)94     public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
95             int which) {
96         if (uid == null) {
97             return 0;
98         }
99 
100         switch (type) {
101             case StatusType.SCREEN_USAGE:
102                 return getScreenUsageTimeMs(uid, which);
103             case StatusType.FOREGROUND:
104                 return getProcessForegroundTimeMs(uid, which);
105             case StatusType.BACKGROUND:
106                 return getProcessBackgroundTimeMs(uid, which);
107             case StatusType.ALL:
108                 return getProcessForegroundTimeMs(uid, which)
109                         + getProcessBackgroundTimeMs(uid, which);
110         }
111         return 0;
112     }
113 
getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs)114     private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which, long rawRealTimeUs) {
115         final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP};
116         Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid()));
117 
118         long timeUs = 0;
119         for (int type : foregroundTypes) {
120             final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which);
121             Log.v(TAG, "type: " + type + " time(us): " + localTime);
122             timeUs += localTime;
123         }
124         Log.v(TAG, "foreground time(us): " + timeUs);
125 
126         // Return the min value of STATE_TOP time and foreground activity time, since both of these
127         // time have some errors
128         return convertUsToMs(
129                 Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)));
130     }
131 
getScreenUsageTimeMs(BatteryStats.Uid uid, int which)132     private long getScreenUsageTimeMs(BatteryStats.Uid uid, int which) {
133         final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
134         return getScreenUsageTimeMs(uid, which, rawRealTimeUs);
135     }
136 
getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which)137     private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) {
138         final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
139         final long timeUs = uid.getProcessStateTime(
140                 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which);
141 
142         Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid()));
143         Log.v(TAG, "background time(us): " + timeUs);
144         return convertUsToMs(timeUs);
145     }
146 
getProcessForegroundTimeMs(BatteryStats.Uid uid, int which)147     private long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
148         final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
149         return getScreenUsageTimeMs(uid, which, rawRealTimeUs)
150                 + convertUsToMs(getForegroundServiceTotalTimeUs(uid, rawRealTimeUs));
151     }
152 
153     /**
154      * Remove the {@link BatterySipper} that we should hide and smear the screen usage based on
155      * foreground activity time.
156      *
157      * @param sippers sipper list that need to check and remove
158      * @return the total power of the hidden items of {@link BatterySipper}
159      * for proportional smearing
160      */
removeHiddenBatterySippers(List<BatterySipper> sippers)161     public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
162         double proportionalSmearPowerMah = 0;
163         BatterySipper screenSipper = null;
164         for (int i = sippers.size() - 1; i >= 0; i--) {
165             final BatterySipper sipper = sippers.get(i);
166             if (shouldHideSipper(sipper)) {
167                 sippers.remove(i);
168                 if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
169                         && sipper.drainType != BatterySipper.DrainType.SCREEN
170                         && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED
171                         && sipper.drainType != BatterySipper.DrainType.BLUETOOTH
172                         && sipper.drainType != BatterySipper.DrainType.WIFI
173                         && sipper.drainType != BatterySipper.DrainType.IDLE) {
174                     // Don't add it if it is overcounted, unaccounted, wifi, bluetooth, or screen
175                     proportionalSmearPowerMah += sipper.totalPowerMah;
176                 }
177             }
178 
179             if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
180                 screenSipper = sipper;
181             }
182         }
183 
184         smearScreenBatterySipper(sippers, screenSipper);
185 
186         return proportionalSmearPowerMah;
187     }
188 
189     /**
190      * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
191      * time.
192      */
193     @VisibleForTesting
smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper)194     void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
195         long totalActivityTimeMs = 0;
196         final SparseLongArray activityTimeArray = new SparseLongArray();
197         for (int i = 0, size = sippers.size(); i < size; i++) {
198             final BatteryStats.Uid uid = sippers.get(i).uidObj;
199             if (uid != null) {
200                 final long timeMs = getProcessTimeMs(StatusType.SCREEN_USAGE, uid,
201                         BatteryStats.STATS_SINCE_CHARGED);
202                 activityTimeArray.put(uid.getUid(), timeMs);
203                 totalActivityTimeMs += timeMs;
204             }
205         }
206 
207         if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
208             final double screenPowerMah = screenSipper.totalPowerMah;
209             for (int i = 0, size = sippers.size(); i < size; i++) {
210                 final BatterySipper sipper = sippers.get(i);
211                 sipper.totalPowerMah += screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
212                         / totalActivityTimeMs;
213             }
214         }
215     }
216 
217     /**
218      * Check whether we should hide the battery sipper.
219      */
shouldHideSipper(BatterySipper sipper)220     public boolean shouldHideSipper(BatterySipper sipper) {
221         final BatterySipper.DrainType drainType = sipper.drainType;
222 
223         return drainType == BatterySipper.DrainType.IDLE
224                 || drainType == BatterySipper.DrainType.CELL
225                 || drainType == BatterySipper.DrainType.SCREEN
226                 || drainType == BatterySipper.DrainType.UNACCOUNTED
227                 || drainType == BatterySipper.DrainType.OVERCOUNTED
228                 || drainType == BatterySipper.DrainType.BLUETOOTH
229                 || drainType == BatterySipper.DrainType.WIFI
230                 || (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP
231                 || mPowerUsageFeatureProvider.isTypeService(sipper)
232                 || mPowerUsageFeatureProvider.isTypeSystem(sipper);
233     }
234 
235     /**
236      * Calculate the power usage percentage for an app
237      *
238      * @param powerUsageMah   power used by the app
239      * @param totalPowerMah   total power used in the system
240      * @param hiddenPowerMah  power used by no-actionable app that we want to hide, i.e. Screen,
241      *                        Android OS.
242      * @param dischargeAmount The discharge amount calculated by {@link BatteryStats}
243      * @return A percentage value scaled by {@paramref dischargeAmount}
244      * @see BatteryStats#getDischargeAmount(int)
245      */
calculateBatteryPercent(double powerUsageMah, double totalPowerMah, double hiddenPowerMah, int dischargeAmount)246     public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah,
247             double hiddenPowerMah, int dischargeAmount) {
248         if (totalPowerMah == 0) {
249             return 0;
250         }
251 
252         return (powerUsageMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount;
253     }
254 
255     /**
256      * Calculate the whole running time in the state {@code statsType}
257      *
258      * @param batteryStatsHelper utility class that contains the data
259      * @param statsType          state that we want to calculate the time for
260      * @return the running time in millis
261      */
calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper, int statsType)262     public long calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper,
263             int statsType) {
264         final long elapsedRealtimeUs = convertMsToUs(SystemClock.elapsedRealtime());
265         // Return the battery time (millisecond) on status mStatsType
266         return convertUsToMs(
267                 batteryStatsHelper.getStats().computeBatteryRealtime(elapsedRealtimeUs, statsType));
268 
269     }
270 
271     /**
272      * Find the package name for a {@link android.os.BatteryStats.Uid}
273      *
274      * @param uid id to get the package name
275      * @return the package name. If there are multiple packages related to
276      * given id, return the first one. Or return null if there are no known
277      * packages with the given id
278      * @see PackageManager#getPackagesForUid(int)
279      */
getPackageName(int uid)280     public String getPackageName(int uid) {
281         final String[] packageNames = mPackageManager.getPackagesForUid(uid);
282 
283         return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0];
284     }
285 
286     /**
287      * Find the targetSdkVersion for package with name {@code packageName}
288      *
289      * @return the targetSdkVersion, or {@link #SDK_NULL} if {@code packageName} doesn't exist
290      */
getTargetSdkVersion(final String packageName)291     public int getTargetSdkVersion(final String packageName) {
292         try {
293             ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
294                     PackageManager.GET_META_DATA);
295 
296             return info.targetSdkVersion;
297         } catch (PackageManager.NameNotFoundException e) {
298             Log.e(TAG, "Cannot find package: " + packageName, e);
299         }
300 
301         return SDK_NULL;
302     }
303 
304     /**
305      * Check whether background restriction is enabled
306      */
isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid, final String packageName)307     public boolean isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid,
308             final String packageName) {
309         if (targetSdkVersion >= Build.VERSION_CODES.O) {
310             return true;
311         }
312         final int mode = mAppOpsManager
313                 .checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName);
314         return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED;
315     }
316 
317     /**
318      * Sort the {@code usageList} based on {@link BatterySipper#totalPowerMah}
319      */
sortUsageList(List<BatterySipper> usageList)320     public void sortUsageList(List<BatterySipper> usageList) {
321         Collections.sort(usageList, new Comparator<BatterySipper>() {
322             @Override
323             public int compare(BatterySipper a, BatterySipper b) {
324                 return Double.compare(b.totalPowerMah, a.totalPowerMah);
325             }
326         });
327     }
328 
329     /**
330      * Calculate the time since last full charge, including the device off time
331      *
332      * @param batteryStatsHelper utility class that contains the data
333      * @param currentTimeMs      current wall time
334      * @return time in millis
335      */
calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper, long currentTimeMs)336     public long calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper,
337             long currentTimeMs) {
338         return currentTimeMs - batteryStatsHelper.getStats().getStartClockTime();
339 
340     }
341 
logRuntime(String tag, String message, long startTime)342     public static void logRuntime(String tag, String message, long startTime) {
343         Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms");
344     }
345 
346     /**
347      * Find package uid from package name
348      *
349      * @param packageName used to find the uid
350      * @return uid for packageName, or {@link #UID_NULL} if exception happens or
351      * {@code packageName} is null
352      */
getPackageUid(String packageName)353     public int getPackageUid(String packageName) {
354         try {
355             return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName,
356                     PackageManager.GET_META_DATA);
357         } catch (PackageManager.NameNotFoundException e) {
358             return UID_NULL;
359         }
360     }
361 
362     @StringRes
getSummaryResIdFromAnomalyType(@nomaly.AnomalyType int type)363     public int getSummaryResIdFromAnomalyType(@Anomaly.AnomalyType int type) {
364         switch (type) {
365             case Anomaly.AnomalyType.WAKE_LOCK:
366                 return R.string.battery_abnormal_wakelock_summary;
367             case Anomaly.AnomalyType.WAKEUP_ALARM:
368                 return R.string.battery_abnormal_wakeup_alarm_summary;
369             case Anomaly.AnomalyType.BLUETOOTH_SCAN:
370                 return R.string.battery_abnormal_location_summary;
371             default:
372                 throw new IllegalArgumentException("Incorrect anomaly type: " + type);
373         }
374     }
375 
convertUsToMs(long timeUs)376     public static long convertUsToMs(long timeUs) {
377         return timeUs / 1000;
378     }
379 
convertMsToUs(long timeMs)380     public static long convertMsToUs(long timeMs) {
381         return timeMs * 1000;
382     }
383 
initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle, UserManager userManager)384     public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle,
385             UserManager userManager) {
386         statsHelper.create(bundle);
387         statsHelper.clearStats();
388         statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, userManager.getUserProfiles());
389     }
390 
isDataCorrupted()391     private boolean isDataCorrupted() {
392         return mPackageManager == null || mAppOpsManager == null;
393     }
394 
395     @VisibleForTesting
getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)396     long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
397         final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
398         if (timer != null) {
399             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
400         }
401 
402         return 0;
403     }
404 
405     @VisibleForTesting
getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs)406     long getForegroundServiceTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
407         final BatteryStats.Timer timer = uid.getForegroundServiceTimer();
408         if (timer != null) {
409             return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
410         }
411 
412         return 0;
413     }
414 
415 }
416 
417