OSDN Git Service

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