1 /*
2  * Copyright (C) 2024 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.content.pm.PackageManager;
20 import android.hardware.power.stats.EnergyConsumerType;
21 import android.net.NetworkStats;
22 import android.os.BatteryConsumer;
23 import android.os.BatteryStats;
24 import android.os.Handler;
25 import android.os.OutcomeReceiver;
26 import android.os.PersistableBundle;
27 import android.telephony.AccessNetworkConstants;
28 import android.telephony.ModemActivityInfo;
29 import android.telephony.ServiceState;
30 import android.telephony.TelephonyManager;
31 import android.util.Slog;
32 import android.util.SparseArray;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.internal.os.Clock;
36 import com.android.internal.os.PowerStats;
37 
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.concurrent.CompletableFuture;
41 import java.util.concurrent.TimeUnit;
42 import java.util.function.IntSupplier;
43 import java.util.function.LongSupplier;
44 import java.util.function.Supplier;
45 
46 public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
47     private static final String TAG = "MobileRadioPowerStatsCollector";
48 
49     /**
50      * The soonest the Mobile Radio stats can be updated due to a mobile radio power state change
51      * after it was last updated.
52      */
53     @VisibleForTesting
54     protected static final long MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS = 1000 * 60 * 10;
55 
56     private static final long MODEM_ACTIVITY_REQUEST_TIMEOUT = 20000;
57 
58     private static final long ENERGY_UNSPECIFIED = -1;
59 
60     @VisibleForTesting
61     @AccessNetworkConstants.RadioAccessNetworkType
62     static final int[] NETWORK_TYPES = {
63             AccessNetworkConstants.AccessNetworkType.UNKNOWN,
64             AccessNetworkConstants.AccessNetworkType.GERAN,
65             AccessNetworkConstants.AccessNetworkType.UTRAN,
66             AccessNetworkConstants.AccessNetworkType.EUTRAN,
67             AccessNetworkConstants.AccessNetworkType.CDMA2000,
68             AccessNetworkConstants.AccessNetworkType.IWLAN,
69             AccessNetworkConstants.AccessNetworkType.NGRAN
70     };
71 
72     interface Injector {
getHandler()73         Handler getHandler();
getClock()74         Clock getClock();
getUidResolver()75         PowerStatsUidResolver getUidResolver();
getPowerStatsCollectionThrottlePeriod(String powerComponentName)76         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
getPackageManager()77         PackageManager getPackageManager();
getConsumedEnergyRetriever()78         ConsumedEnergyRetriever getConsumedEnergyRetriever();
getVoltageSupplier()79         IntSupplier getVoltageSupplier();
getMobileNetworkStatsSupplier()80         Supplier<NetworkStats> getMobileNetworkStatsSupplier();
getTelephonyManager()81         TelephonyManager getTelephonyManager();
getCallDurationSupplier()82         LongSupplier getCallDurationSupplier();
getPhoneSignalScanDurationSupplier()83         LongSupplier getPhoneSignalScanDurationSupplier();
84     }
85 
86     private final Injector mInjector;
87 
88     private MobileRadioPowerStatsLayout mLayout;
89     private boolean mIsInitialized;
90 
91     private PowerStats mPowerStats;
92     private long[] mDeviceStats;
93     private volatile TelephonyManager mTelephonyManager;
94     private LongSupplier mCallDurationSupplier;
95     private LongSupplier mScanDurationSupplier;
96     private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
97     private ConsumedEnergyRetriever mConsumedEnergyRetriever;
98     private IntSupplier mVoltageSupplier;
99     private int[] mEnergyConsumerIds = new int[0];
100     private long mLastUpdateTimestampMillis;
101     private ModemActivityInfo mLastModemActivityInfo;
102     private NetworkStats mLastNetworkStats;
103     private long[] mLastConsumedEnergyUws;
104     private int mLastVoltageMv;
105     private long mLastCallDuration;
106     private long mLastScanDuration;
107 
MobileRadioPowerStatsCollector(Injector injector)108     MobileRadioPowerStatsCollector(Injector injector) {
109         super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
110                         BatteryConsumer.powerComponentIdToString(
111                                 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)),
112                 injector.getUidResolver(),
113                 injector.getClock());
114         mInjector = injector;
115     }
116 
117     @Override
setEnabled(boolean enabled)118     public void setEnabled(boolean enabled) {
119         if (enabled) {
120             PackageManager packageManager = mInjector.getPackageManager();
121             super.setEnabled(packageManager != null
122                     && packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
123         } else {
124             super.setEnabled(false);
125         }
126     }
127 
ensureInitialized()128     private boolean ensureInitialized() {
129         if (mIsInitialized) {
130             return true;
131         }
132 
133         if (!isEnabled()) {
134             return false;
135         }
136 
137         mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
138         mVoltageSupplier = mInjector.getVoltageSupplier();
139 
140         mTelephonyManager = mInjector.getTelephonyManager();
141         mNetworkStatsSupplier = mInjector.getMobileNetworkStatsSupplier();
142         mCallDurationSupplier = mInjector.getCallDurationSupplier();
143         mScanDurationSupplier = mInjector.getPhoneSignalScanDurationSupplier();
144 
145         mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
146                 EnergyConsumerType.MOBILE_RADIO);
147         mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
148         Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
149 
150         mLayout = new MobileRadioPowerStatsLayout();
151         mLayout.addDeviceMobileActivity();
152         mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
153         mLayout.addStateStats();
154         mLayout.addUidNetworkStats();
155         mLayout.addDeviceSectionUsageDuration();
156         mLayout.addDeviceSectionPowerEstimate();
157         mLayout.addUidSectionPowerEstimate();
158 
159         SparseArray<String> stateLabels = new SparseArray<>();
160         for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
161             final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
162                     ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
163             for (int freq = 0; freq < freqCount; freq++) {
164                 int stateKey = makeStateKey(rat, freq);
165                 StringBuilder sb = new StringBuilder();
166                 if (rat != BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER) {
167                     sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
168                 }
169                 if (freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
170                     if (!sb.isEmpty()) {
171                         sb.append(" ");
172                     }
173                     sb.append(ServiceState.frequencyRangeToString(freq));
174                 }
175                 stateLabels.put(stateKey, !sb.isEmpty() ? sb.toString() : "other");
176             }
177         }
178 
179         PersistableBundle extras = new PersistableBundle();
180         mLayout.toExtras(extras);
181         PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
182                 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, mLayout.getDeviceStatsArrayLength(),
183                 stateLabels, mLayout.getStateStatsArrayLength(), mLayout.getUidStatsArrayLength(),
184                 extras);
185         mPowerStats = new PowerStats(powerStatsDescriptor);
186         mDeviceStats = mPowerStats.stats;
187 
188         mIsInitialized = true;
189         return true;
190     }
191 
192     @Override
collectStats()193     protected PowerStats collectStats() {
194         if (!ensureInitialized()) {
195             return null;
196         }
197 
198         Arrays.fill(mPowerStats.stats, 0);
199         mPowerStats.uidStats.clear();
200 
201         collectModemActivityInfo();
202 
203         collectNetworkStats();
204 
205         if (mEnergyConsumerIds.length != 0) {
206             collectEnergyConsumers();
207         }
208 
209         if (mPowerStats.durationMs == 0) {
210             setTimestamp(mClock.elapsedRealtime());
211         }
212 
213         return mPowerStats;
214     }
215 
collectModemActivityInfo()216     private void collectModemActivityInfo() {
217         if (mTelephonyManager == null) {
218             return;
219         }
220 
221         CompletableFuture<ModemActivityInfo> immediateFuture = new CompletableFuture<>();
222         mTelephonyManager.requestModemActivityInfo(Runnable::run,
223                 new OutcomeReceiver<>() {
224                     @Override
225                     public void onResult(ModemActivityInfo result) {
226                         immediateFuture.complete(result);
227                     }
228 
229                     @Override
230                     public void onError(TelephonyManager.ModemActivityInfoException e) {
231                         Slog.w(TAG, "error reading modem stats:" + e);
232                         immediateFuture.complete(null);
233                     }
234                 });
235 
236         ModemActivityInfo activityInfo;
237         try {
238             activityInfo = immediateFuture.get(MODEM_ACTIVITY_REQUEST_TIMEOUT,
239                     TimeUnit.MILLISECONDS);
240         } catch (Exception e) {
241             Slog.e(TAG, "Cannot acquire ModemActivityInfo");
242             activityInfo = null;
243         }
244 
245         if (activityInfo == null) {
246             return;
247         }
248 
249         ModemActivityInfo deltaInfo = mLastModemActivityInfo == null
250                 ? activityInfo.getDelta(activityInfo)
251                 : mLastModemActivityInfo.getDelta(activityInfo);
252 
253         mLastModemActivityInfo = activityInfo;
254 
255         setTimestamp(deltaInfo.getTimestampMillis());
256         mLayout.setDeviceSleepTime(mDeviceStats, deltaInfo.getSleepTimeMillis());
257         mLayout.setDeviceIdleTime(mDeviceStats, deltaInfo.getIdleTimeMillis());
258 
259         long callDuration = mCallDurationSupplier.getAsLong();
260         if (callDuration >= mLastCallDuration) {
261             mLayout.setDeviceCallTime(mDeviceStats, callDuration - mLastCallDuration);
262         }
263         mLastCallDuration = callDuration;
264 
265         long scanDuration = mScanDurationSupplier.getAsLong();
266         if (scanDuration >= mLastScanDuration) {
267             mLayout.setDeviceScanTime(mDeviceStats, scanDuration - mLastScanDuration);
268         }
269         mLastScanDuration = scanDuration;
270 
271         SparseArray<long[]> stateStats = mPowerStats.stateStats;
272         stateStats.clear();
273 
274         if (deltaInfo.getSpecificInfoLength() == 0) {
275             mLayout.addRxTxTimesForRat(stateStats,
276                     AccessNetworkConstants.AccessNetworkType.UNKNOWN,
277                     ServiceState.FREQUENCY_RANGE_UNKNOWN,
278                     deltaInfo.getReceiveTimeMillis(),
279                     deltaInfo.getTransmitTimeMillis());
280         } else {
281             for (int rat = 0; rat < NETWORK_TYPES.length; rat++) {
282                 if (rat == AccessNetworkConstants.AccessNetworkType.NGRAN) {
283                     for (int freq = 0; freq < ServiceState.FREQUENCY_RANGE_COUNT; freq++) {
284                         mLayout.addRxTxTimesForRat(stateStats, rat, freq,
285                                 deltaInfo.getReceiveTimeMillis(rat, freq),
286                                 deltaInfo.getTransmitTimeMillis(rat, freq));
287                     }
288                 } else {
289                     mLayout.addRxTxTimesForRat(stateStats, rat,
290                             ServiceState.FREQUENCY_RANGE_UNKNOWN,
291                             deltaInfo.getReceiveTimeMillis(rat),
292                             deltaInfo.getTransmitTimeMillis(rat));
293                 }
294             }
295         }
296     }
297 
collectNetworkStats()298     private void collectNetworkStats() {
299         NetworkStats networkStats = mNetworkStatsSupplier.get();
300         if (networkStats == null) {
301             return;
302         }
303 
304         List<BatteryStatsImpl.NetworkStatsDelta> delta =
305                 BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
306         mLastNetworkStats = networkStats;
307         for (int i = delta.size() - 1; i >= 0; i--) {
308             BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
309             long rxBytes = uidDelta.getRxBytes();
310             long txBytes = uidDelta.getTxBytes();
311             long rxPackets = uidDelta.getRxPackets();
312             long txPackets = uidDelta.getTxPackets();
313             if (rxBytes == 0 && txBytes == 0 && rxPackets == 0 && txPackets == 0) {
314                 continue;
315             }
316 
317             int uid = mUidResolver.mapUid(uidDelta.getUid());
318             long[] stats = mPowerStats.uidStats.get(uid);
319             if (stats == null) {
320                 stats = new long[mLayout.getUidStatsArrayLength()];
321                 mPowerStats.uidStats.put(uid, stats);
322                 mLayout.setUidRxBytes(stats, rxBytes);
323                 mLayout.setUidTxBytes(stats, txBytes);
324                 mLayout.setUidRxPackets(stats, rxPackets);
325                 mLayout.setUidTxPackets(stats, txPackets);
326             } else {
327                 mLayout.setUidRxBytes(stats, mLayout.getUidRxBytes(stats) + rxBytes);
328                 mLayout.setUidTxBytes(stats, mLayout.getUidTxBytes(stats) + txBytes);
329                 mLayout.setUidRxPackets(stats, mLayout.getUidRxPackets(stats) + rxPackets);
330                 mLayout.setUidTxPackets(stats, mLayout.getUidTxPackets(stats) + txPackets);
331             }
332         }
333     }
334 
collectEnergyConsumers()335     private void collectEnergyConsumers() {
336         int voltageMv = mVoltageSupplier.getAsInt();
337         if (voltageMv <= 0) {
338             Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
339                     + " mV) when querying energy consumers");
340             return;
341         }
342 
343         int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
344         mLastVoltageMv = voltageMv;
345 
346         long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
347         if (energyUws == null) {
348             return;
349         }
350 
351         for (int i = energyUws.length - 1; i >= 0; i--) {
352             long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
353                     ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
354             if (energyDelta < 0) {
355                 // Likely, restart of powerstats HAL
356                 energyDelta = 0;
357             }
358             mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
359             mLastConsumedEnergyUws[i] = energyUws[i];
360         }
361     }
362 
makeStateKey(int rat, int freqRange)363     static int makeStateKey(int rat, int freqRange) {
364         if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
365             return rat | (freqRange << 8);
366         } else {
367             return rat;
368         }
369     }
370 
setTimestamp(long timestamp)371     private void setTimestamp(long timestamp) {
372         mPowerStats.durationMs = Math.max(timestamp - mLastUpdateTimestampMillis, 0);
373         mLastUpdateTimestampMillis = timestamp;
374     }
375 
376     @BatteryStats.RadioAccessTechnology
mapRadioAccessNetworkTypeToRadioAccessTechnology( @ccessNetworkConstants.RadioAccessNetworkType int networkType)377     static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
378             @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
379         switch (networkType) {
380             case AccessNetworkConstants.AccessNetworkType.NGRAN:
381                 return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
382             case AccessNetworkConstants.AccessNetworkType.EUTRAN:
383                 return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
384             case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
385             case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
386             case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
387             case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
388             case AccessNetworkConstants.AccessNetworkType.IWLAN:
389                 return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
390             default:
391                 Slog.w(TAG,
392                         "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
393                 return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
394         }
395     }
396 }
397