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.server.power.stats;
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.util.Log;
26 import android.util.SparseArray;
27 
28 import com.android.internal.os.PowerProfile;
29 
30 /**
31  * Estimates the amount of power consumed by the System Server handling requests from
32  * a given app.
33  */
34 public class SystemServicePowerCalculator extends PowerCalculator {
35     private static final boolean DEBUG = false;
36     private static final String TAG = "SystemServicePowerCalc";
37 
38     // Power estimators per CPU cluster, per CPU frequency. The array is flattened according
39     // to this layout:
40     // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
41     private final UsageBasedPowerEstimator[] mPowerEstimators;
42     private final com.android.server.power.stats.CpuPowerCalculator mCpuPowerCalculator;
43 
SystemServicePowerCalculator(PowerProfile powerProfile)44     public SystemServicePowerCalculator(PowerProfile powerProfile) {
45         mCpuPowerCalculator = new CpuPowerCalculator(powerProfile);
46         int numFreqs = 0;
47         final int numCpuClusters = powerProfile.getNumCpuClusters();
48         for (int cluster = 0; cluster < numCpuClusters; cluster++) {
49             numFreqs += powerProfile.getNumSpeedStepsInCpuCluster(cluster);
50         }
51 
52         mPowerEstimators = new UsageBasedPowerEstimator[numFreqs];
53         int index = 0;
54         for (int cluster = 0; cluster < numCpuClusters; cluster++) {
55             final int numSpeeds = powerProfile.getNumSpeedStepsInCpuCluster(cluster);
56             for (int speed = 0; speed < numSpeeds; speed++) {
57                 mPowerEstimators[index++] = new UsageBasedPowerEstimator(
58                         powerProfile.getAveragePowerForCpuCore(cluster, speed));
59             }
60         }
61     }
62 
63     @Override
isPowerComponentSupported(@atteryConsumer.PowerComponent int powerComponent)64     public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
65         return powerComponent == BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES;
66     }
67 
68     @Override
calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query)69     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
70             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
71         final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
72         if (systemUid == null) {
73             return;
74         }
75 
76         final long consumptionUC = systemUid.getCpuEnergyConsumptionUC();
77         final int powerModel = getPowerModel(consumptionUC, query);
78 
79         double systemServicePowerMah;
80         if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
81             systemServicePowerMah = calculatePowerUsingEnergyConsumption(batteryStats,
82                     systemUid, consumptionUC);
83         } else {
84             systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
85         }
86 
87         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
88                 builder.getUidBatteryConsumerBuilders();
89         final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
90                 Process.SYSTEM_UID);
91 
92         if (systemServerConsumer != null) {
93             systemServicePowerMah = Math.min(systemServicePowerMah,
94                     systemServerConsumer.getTotalPower());
95 
96             // The system server power needs to be adjusted because part of it got
97             // distributed to applications
98             systemServerConsumer.setConsumedPower(
99                     BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
100                     -systemServicePowerMah, powerModel);
101         }
102 
103         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
104             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
105             if (app != systemServerConsumer) {
106                 final BatteryStats.Uid uid = app.getBatteryStatsUid();
107                 app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
108                         systemServicePowerMah * uid.getProportionalSystemServiceUsage(),
109                         powerModel);
110             }
111         }
112 
113         builder.getAggregateBatteryConsumerBuilder(
114                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
115                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
116                         systemServicePowerMah);
117         builder.getAggregateBatteryConsumerBuilder(
118                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
119                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
120                         systemServicePowerMah);
121     }
122 
calculatePowerUsingEnergyConsumption(BatteryStats batteryStats, BatteryStats.Uid systemUid, long consumptionUC)123     private double calculatePowerUsingEnergyConsumption(BatteryStats batteryStats,
124             BatteryStats.Uid systemUid, long consumptionUC) {
125         // Use the PowerProfile based model to estimate the ratio between the power consumed
126         // while handling incoming binder calls and the entire System UID power consumption.
127         // Apply that ratio to the _EnergyConsumer_ system UID power consumption to get a more
128         // accurate estimate of the power consumed by incoming binder calls.
129         final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
130         final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
131                 systemUid, BatteryStats.STATS_SINCE_CHARGED);
132 
133         if (systemUidModeledPowerMah > 0) {
134             return uCtoMah(consumptionUC) * systemServiceModeledPowerMah / systemUidModeledPowerMah;
135         } else {
136             return 0;
137         }
138     }
139 
calculatePowerUsingPowerProfile(BatteryStats batteryStats)140     private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
141         final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
142         if (systemServiceTimeAtCpuSpeeds == null) {
143             return 0;
144         }
145 
146         // TODO(179210707): additionally account for CPU active and per cluster battery use
147 
148         double powerMah = 0;
149         final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length);
150         for (int i = 0; i < size; i++) {
151             powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000);
152         }
153 
154         if (DEBUG) {
155             Log.d(TAG, "System service power:" + powerMah);
156         }
157         return powerMah;
158     }
159 }
160