1 /*
2  * Copyright (C) 2020 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.internal.os;
18 
19 import android.os.BatteryConsumer;
20 import android.os.BatteryStats;
21 import android.os.BatteryUsageStats;
22 import android.os.BatteryUsageStatsQuery;
23 import android.os.Process;
24 import android.os.UidBatteryConsumer;
25 import android.os.UserHandle;
26 import android.util.Log;
27 import android.util.SparseArray;
28 
29 import java.util.List;
30 
31 /**
32  * Estimates the amount of power consumed by the System Server handling requests from
33  * a given app.
34  */
35 public class SystemServicePowerCalculator extends PowerCalculator {
36     private static final boolean DEBUG = false;
37     private static final String TAG = "SystemServicePowerCalc";
38 
39     // Power estimators per CPU cluster, per CPU frequency. The array is flattened according
40     // to this layout:
41     // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
42     private final UsageBasedPowerEstimator[] mPowerEstimators;
43     private final CpuPowerCalculator mCpuPowerCalculator;
44 
SystemServicePowerCalculator(PowerProfile powerProfile)45     public SystemServicePowerCalculator(PowerProfile powerProfile) {
46         mCpuPowerCalculator = new CpuPowerCalculator(powerProfile);
47         int numFreqs = 0;
48         final int numCpuClusters = powerProfile.getNumCpuClusters();
49         for (int cluster = 0; cluster < numCpuClusters; cluster++) {
50             numFreqs += powerProfile.getNumSpeedStepsInCpuCluster(cluster);
51         }
52 
53         mPowerEstimators = new UsageBasedPowerEstimator[numFreqs];
54         int index = 0;
55         for (int cluster = 0; cluster < numCpuClusters; cluster++) {
56             final int numSpeeds = powerProfile.getNumSpeedStepsInCpuCluster(cluster);
57             for (int speed = 0; speed < numSpeeds; speed++) {
58                 mPowerEstimators[index++] = new UsageBasedPowerEstimator(
59                         powerProfile.getAveragePowerForCpuCore(cluster, speed));
60             }
61         }
62     }
63 
64     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)65     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
66             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
67         final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
68         if (systemUid == null) {
69             return;
70         }
71 
72         final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
73         final int powerModel = getPowerModel(consumptionUC, query);
74 
75         double systemServicePowerMah;
76         if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
77             systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
78                     systemUid, consumptionUC);
79         } else {
80             systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
81         }
82 
83         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
84                 builder.getUidBatteryConsumerBuilders();
85         final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
86                 Process.SYSTEM_UID);
87 
88         if (systemServerConsumer != null) {
89             systemServicePowerMah = Math.min(systemServicePowerMah,
90                     systemServerConsumer.getTotalPower());
91 
92             // The system server power needs to be adjusted because part of it got
93             // distributed to applications
94             systemServerConsumer.setConsumedPower(
95                     BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
96                     -systemServicePowerMah, powerModel);
97         }
98 
99         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
100             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
101             if (app != systemServerConsumer) {
102                 final BatteryStats.Uid uid = app.getBatteryStatsUid();
103                 app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
104                         systemServicePowerMah * uid.getProportionalSystemServiceUsage(),
105                         powerModel);
106             }
107         }
108 
109         builder.getAggregateBatteryConsumerBuilder(
110                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
111                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
112                         systemServicePowerMah);
113         builder.getAggregateBatteryConsumerBuilder(
114                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
115                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
116                         systemServicePowerMah);
117     }
118 
119     @Override
calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers)120     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
121             long rawRealtimeUs, long rawUptimeUs, int statsType,
122             SparseArray<UserHandle> asUsers) {
123         final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
124         if (systemUid == null) {
125             return;
126         }
127 
128         final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
129         double systemServicePowerMah;
130         if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
131             systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
132                     systemUid, consumptionUC);
133         } else {
134             systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
135         }
136 
137         BatterySipper systemServerSipper = null;
138         for (int i = sippers.size() - 1; i >= 0; i--) {
139             final BatterySipper app = sippers.get(i);
140             if (app.drainType == BatterySipper.DrainType.APP) {
141                 if (app.getUid() == Process.SYSTEM_UID) {
142                     systemServerSipper = app;
143                     break;
144                 }
145             }
146         }
147 
148         if (systemServerSipper != null) {
149             systemServicePowerMah = Math.min(systemServicePowerMah, systemServerSipper.sumPower());
150 
151             // The system server power needs to be adjusted because part of it got
152             // distributed to applications
153             systemServerSipper.powerReattributedToOtherSippersMah = -systemServicePowerMah;
154         }
155 
156         for (int i = sippers.size() - 1; i >= 0; i--) {
157             final BatterySipper app = sippers.get(i);
158             if (app.drainType == BatterySipper.DrainType.APP) {
159                 if (app != systemServerSipper) {
160                     final BatteryStats.Uid uid = app.uidObj;
161                     app.systemServiceCpuPowerMah =
162                             systemServicePowerMah * uid.getProportionalSystemServiceUsage();
163                 }
164             }
165         }
166     }
167 
calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats, BatteryStats.Uid systemUid, long consumptionUC)168     private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats,
169             BatteryStats.Uid systemUid, long consumptionUC) {
170         // Use the PowerProfile based model to estimate the ratio between the power consumed
171         // while handling incoming binder calls and the entire System UID power consumption.
172         // Apply that ratio to the _measured_ system UID power consumption to get a more
173         // accurate estimate of the power consumed by incoming binder calls.
174         final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
175         final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
176                 systemUid, BatteryStats.STATS_SINCE_CHARGED);
177 
178         return uCtoMah(consumptionUC) * systemServiceModeledPowerMah
179                 / systemUidModeledPowerMah;
180     }
181 
calculatePowerUsingPowerProfile(BatteryStats batteryStats)182     private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
183         final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
184         if (systemServiceTimeAtCpuSpeeds == null) {
185             return 0;
186         }
187 
188         // TODO(179210707): additionally account for CPU active and per cluster battery use
189 
190         double powerMah = 0;
191         final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length);
192         for (int i = 0; i < size; i++) {
193             powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000);
194         }
195 
196         if (DEBUG) {
197             Log.d(TAG, "System service power:" + powerMah);
198         }
199         return powerMah;
200     }
201 }
202