1 /*
2  * Copyright (C) 2015 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.internal.os;
17 
18 import android.os.BatteryStats;
19 import android.util.ArrayMap;
20 import android.util.Log;
21 
22 public class CpuPowerCalculator extends PowerCalculator {
23     private static final String TAG = "CpuPowerCalculator";
24     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
25     private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
26     private final PowerProfile mProfile;
27 
CpuPowerCalculator(PowerProfile profile)28     public CpuPowerCalculator(PowerProfile profile) {
29         mProfile = profile;
30     }
31 
32     @Override
calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType)33     public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
34             long rawUptimeUs, int statsType) {
35         app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
36         final int numClusters = mProfile.getNumCpuClusters();
37 
38         double cpuPowerMaUs = 0;
39         for (int cluster = 0; cluster < numClusters; cluster++) {
40             final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
41             for (int speed = 0; speed < speedsForCluster; speed++) {
42                 final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
43                 final double cpuSpeedStepPower = timeUs *
44                         mProfile.getAveragePowerForCpuCore(cluster, speed);
45                 if (DEBUG) {
46                     Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
47                             + speed + " timeUs=" + timeUs + " power="
48                             + BatteryStatsHelper.makemAh(cpuSpeedStepPower / MICROSEC_IN_HR));
49                 }
50                 cpuPowerMaUs += cpuSpeedStepPower;
51             }
52         }
53         cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower(
54                 PowerProfile.POWER_CPU_ACTIVE);
55         long[] cpuClusterTimes = u.getCpuClusterTimes();
56         if (cpuClusterTimes != null) {
57             if (cpuClusterTimes.length == numClusters) {
58                 for (int i = 0; i < numClusters; i++) {
59                     double power =
60                             cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i);
61                     cpuPowerMaUs += power;
62                     if (DEBUG) {
63                         Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
64                                 + cpuClusterTimes[i] + " power="
65                                 + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR));
66                     }
67                 }
68             } else {
69                 Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
70                         + numClusters + " actual # " + cpuClusterTimes.length);
71             }
72         }
73         app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
74 
75         if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
76             Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
77                     + BatteryStatsHelper.makemAh(app.cpuPowerMah));
78         }
79 
80         // Keep track of the package with highest drain.
81         double highestDrain = 0;
82 
83         app.cpuFgTimeMs = 0;
84         final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
85         final int processStatsCount = processStats.size();
86         for (int i = 0; i < processStatsCount; i++) {
87             final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
88             final String processName = processStats.keyAt(i);
89             app.cpuFgTimeMs += ps.getForegroundTime(statsType);
90 
91             final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
92                     + ps.getForegroundTime(statsType);
93 
94             // Each App can have multiple packages and with multiple running processes.
95             // Keep track of the package who's process has the highest drain.
96             if (app.packageWithHighestDrain == null ||
97                     app.packageWithHighestDrain.startsWith("*")) {
98                 highestDrain = costValue;
99                 app.packageWithHighestDrain = processName;
100             } else if (highestDrain < costValue && !processName.startsWith("*")) {
101                 highestDrain = costValue;
102                 app.packageWithHighestDrain = processName;
103             }
104         }
105 
106         // Ensure that the CPU times make sense.
107         if (app.cpuFgTimeMs > app.cpuTimeMs) {
108             if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) {
109                 Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
110             }
111 
112             // Statistics may not have been gathered yet.
113             app.cpuTimeMs = app.cpuFgTimeMs;
114         }
115     }
116 }
117