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