1 /* 2 * Copyright (C) 2016 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 android.os.health; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemService; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.Context; 25 import android.os.BatteryStats; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.IPowerStatsService; 29 import android.os.OutcomeReceiver; 30 import android.os.PowerMonitor; 31 import android.os.PowerMonitorReadings; 32 import android.os.Process; 33 import android.os.RemoteException; 34 import android.os.ResultReceiver; 35 import android.os.ServiceManager; 36 import android.os.SynchronousResultReceiver; 37 38 import com.android.internal.app.IBatteryStats; 39 import com.android.server.power.optimization.Flags; 40 41 import java.util.Arrays; 42 import java.util.Comparator; 43 import java.util.List; 44 import java.util.concurrent.Executor; 45 import java.util.concurrent.TimeoutException; 46 import java.util.function.Consumer; 47 48 /** 49 * Provides access to data about how various system resources are used by applications. 50 * @more 51 * <p> 52 * If you are going to be using this class to log your application's resource usage, 53 * please consider the amount of resources (battery, network, etc) that will be used 54 * by the logging itself. It can be substantial. 55 * <p> 56 * <b>Battery Usage</b><br> 57 * Since Android version {@link android.os.Build.VERSION_CODES#Q}, the statistics related to power 58 * (battery) usage are recorded since the device was last considered fully charged (for previous 59 * versions, it is instead since the device was last unplugged). 60 * It is expected that applications schedule more work to do while the device is 61 * plugged in (e.g. using {@link android.app.job.JobScheduler JobScheduler}), and 62 * while that can affect charging rates, it is still preferable to actually draining 63 * the battery. 64 */ 65 @SystemService(Context.SYSTEM_HEALTH_SERVICE) 66 public class SystemHealthManager { 67 @NonNull 68 private final IBatteryStats mBatteryStats; 69 @Nullable 70 private final IPowerStatsService mPowerStats; 71 private List<PowerMonitor> mPowerMonitorsInfo; 72 private final Object mPowerMonitorsLock = new Object(); 73 private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000; 74 75 private static class PendingUidSnapshots { 76 public int[] uids; 77 public SynchronousResultReceiver resultReceiver; 78 } 79 80 private final PendingUidSnapshots mPendingUidSnapshots = new PendingUidSnapshots(); 81 82 /** 83 * Construct a new SystemHealthManager object. 84 * 85 * @hide 86 */ 87 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) SystemHealthManager()88 public SystemHealthManager() { 89 this(IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME)), 90 IPowerStatsService.Stub.asInterface( 91 ServiceManager.getService(Context.POWER_STATS_SERVICE))); 92 } 93 94 /** {@hide} */ SystemHealthManager(@onNull IBatteryStats batteryStats, @Nullable IPowerStatsService powerStats)95 public SystemHealthManager(@NonNull IBatteryStats batteryStats, 96 @Nullable IPowerStatsService powerStats) { 97 mBatteryStats = batteryStats; 98 mPowerStats = powerStats; 99 } 100 101 /** 102 * Obtain a SystemHealthManager object for the supplied context. 103 * 104 * @hide 105 */ 106 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) from(Context context)107 public static SystemHealthManager from(Context context) { 108 return (SystemHealthManager) context.getSystemService(Context.SYSTEM_HEALTH_SERVICE); 109 } 110 111 /** 112 * Return a {@link HealthStats} object containing a snapshot of system health 113 * metrics for the given uid (user-id, which in usually corresponds to application). 114 * 115 * @param uid User ID for a given application. 116 * @return A {@link HealthStats} object containing the metrics for the requested 117 * application. The keys for this HealthStats object will be from the {@link UidHealthStats} 118 * class. 119 * @more An application must hold the {@link android.Manifest.permission#BATTERY_STATS 120 * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats 121 * other than its own. 122 * @see Process#myUid() Process.myUid() 123 */ takeUidSnapshot(int uid)124 public HealthStats takeUidSnapshot(int uid) { 125 if (!Flags.onewayBatteryStatsService()) { 126 try { 127 final HealthStatsParceler parceler = mBatteryStats.takeUidSnapshot(uid); 128 return parceler.getHealthStats(); 129 } catch (RemoteException ex) { 130 throw ex.rethrowFromSystemServer(); 131 } 132 } 133 final HealthStats[] result = takeUidSnapshots(new int[]{uid}); 134 if (result != null && result.length >= 1) { 135 return result[0]; 136 } 137 return null; 138 } 139 140 /** 141 * Return a {@link HealthStats} object containing a snapshot of system health 142 * metrics for the application calling this API. This method is the same as calling 143 * {@code takeUidSnapshot(Process.myUid())}. 144 * 145 * @return A {@link HealthStats} object containing the metrics for this application. The keys 146 * for this HealthStats object will be from the {@link UidHealthStats} class. 147 */ takeMyUidSnapshot()148 public HealthStats takeMyUidSnapshot() { 149 return takeUidSnapshot(Process.myUid()); 150 } 151 152 /** 153 * Return a {@link HealthStats} object containing a snapshot of system health 154 * metrics for the given uids (user-id, which in usually corresponds to application). 155 * 156 * @param uids An array of User IDs to retrieve. 157 * @return An array of {@link HealthStats} objects containing the metrics for each of 158 * the requested uids. The keys for this HealthStats object will be from the 159 * {@link UidHealthStats} class. 160 * @more An application must hold the {@link android.Manifest.permission#BATTERY_STATS 161 * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats 162 * other than its own. 163 */ takeUidSnapshots(int[] uids)164 public HealthStats[] takeUidSnapshots(int[] uids) { 165 if (!Flags.onewayBatteryStatsService()) { 166 try { 167 final HealthStatsParceler[] parcelers = mBatteryStats.takeUidSnapshots(uids); 168 final int count = uids.length; 169 final HealthStats[] results = new HealthStats[count]; 170 for (int i = 0; i < count; i++) { 171 results[i] = parcelers[i].getHealthStats(); 172 } 173 return results; 174 } catch (RemoteException ex) { 175 throw ex.rethrowFromSystemServer(); 176 } 177 } 178 179 SynchronousResultReceiver resultReceiver; 180 synchronized (mPendingUidSnapshots) { 181 if (Arrays.equals(mPendingUidSnapshots.uids, uids)) { 182 resultReceiver = mPendingUidSnapshots.resultReceiver; 183 } else { 184 mPendingUidSnapshots.uids = Arrays.copyOf(uids, uids.length); 185 mPendingUidSnapshots.resultReceiver = resultReceiver = 186 new SynchronousResultReceiver("takeUidSnapshots"); 187 try { 188 mBatteryStats.takeUidSnapshotsAsync(uids, resultReceiver); 189 } catch (RemoteException ex) { 190 throw ex.rethrowFromSystemServer(); 191 } 192 } 193 } 194 195 SynchronousResultReceiver.Result result; 196 try { 197 result = resultReceiver.awaitResult(TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS); 198 } catch (TimeoutException e) { 199 throw new RuntimeException(e); 200 } finally { 201 synchronized (mPendingUidSnapshots) { 202 if (mPendingUidSnapshots.resultReceiver == resultReceiver) { 203 mPendingUidSnapshots.uids = null; 204 mPendingUidSnapshots.resultReceiver = null; 205 } 206 } 207 } 208 209 final HealthStats[] results = new HealthStats[uids.length]; 210 if (result.bundle != null) { 211 HealthStatsParceler[] parcelers = result.bundle.getParcelableArray( 212 IBatteryStats.KEY_UID_SNAPSHOTS, HealthStatsParceler.class); 213 if (parcelers != null && parcelers.length == uids.length) { 214 for (int i = 0; i < parcelers.length; i++) { 215 results[i] = parcelers[i].getHealthStats(); 216 } 217 } 218 } 219 return results; 220 } 221 222 /** 223 * Asynchronously retrieves a list of supported {@link PowerMonitor}'s, which include raw ODPM 224 * (on-device power rail monitor) rails and modeled energy consumers. If ODPM is unsupported 225 * on this device this method delivers an empty list. 226 * 227 * @param executor optional Handler to deliver the callback. If not supplied, the callback 228 * may be invoked on an arbitrary thread. 229 * @param onResult callback for the result 230 */ 231 @FlaggedApi("com.android.server.power.optimization.power_monitor_api") getSupportedPowerMonitors(@ullable Executor executor, @NonNull Consumer<List<PowerMonitor>> onResult)232 public void getSupportedPowerMonitors(@Nullable Executor executor, 233 @NonNull Consumer<List<PowerMonitor>> onResult) { 234 final List<PowerMonitor> result; 235 synchronized (mPowerMonitorsLock) { 236 if (mPowerMonitorsInfo != null) { 237 result = mPowerMonitorsInfo; 238 } else if (mPowerStats == null) { 239 mPowerMonitorsInfo = List.of(); 240 result = mPowerMonitorsInfo; 241 } else { 242 result = null; 243 } 244 } 245 if (result != null) { 246 if (executor != null) { 247 executor.execute(() -> onResult.accept(result)); 248 } else { 249 onResult.accept(result); 250 } 251 return; 252 } 253 try { 254 mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) { 255 @Override 256 protected void onReceiveResult(int resultCode, Bundle resultData) { 257 PowerMonitor[] array = resultData.getParcelableArray( 258 IPowerStatsService.KEY_MONITORS, PowerMonitor.class); 259 List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of(); 260 synchronized (mPowerMonitorsLock) { 261 mPowerMonitorsInfo = result; 262 } 263 if (executor != null) { 264 executor.execute(()-> onResult.accept(result)); 265 } else { 266 onResult.accept(result); 267 } 268 } 269 }); 270 } catch (RemoteException e) { 271 throw e.rethrowFromSystemServer(); 272 } 273 } 274 275 private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR = 276 Comparator.comparingInt(pm -> pm.index); 277 278 /** 279 * Asynchronously retrieves the accumulated power consumption reported by the specified power 280 * monitors. 281 * 282 * @param powerMonitors power monitors to be retrieved. 283 * @param executor optional Executor to deliver the callbacks. If not supplied, the 284 * callback may be invoked on an arbitrary thread. 285 * @param onResult callback for the result 286 */ 287 @FlaggedApi("com.android.server.power.optimization.power_monitor_api") getPowerMonitorReadings(@onNull List<PowerMonitor> powerMonitors, @Nullable Executor executor, @NonNull OutcomeReceiver<PowerMonitorReadings, RuntimeException> onResult)288 public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors, 289 @Nullable Executor executor, 290 @NonNull OutcomeReceiver<PowerMonitorReadings, RuntimeException> onResult) { 291 if (mPowerStats == null) { 292 IllegalArgumentException error = 293 new IllegalArgumentException("Unsupported power monitor"); 294 if (executor != null) { 295 executor.execute(() -> onResult.onError(error)); 296 } else { 297 onResult.onError(error); 298 } 299 return; 300 } 301 302 PowerMonitor[] powerMonitorsArray = 303 powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]); 304 Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR); 305 int[] indices = new int[powerMonitors.size()]; 306 for (int i = 0; i < powerMonitors.size(); i++) { 307 indices[i] = powerMonitorsArray[i].index; 308 } 309 try { 310 mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) { 311 @Override 312 protected void onReceiveResult(int resultCode, Bundle resultData) { 313 if (resultCode == IPowerStatsService.RESULT_SUCCESS) { 314 PowerMonitorReadings result = new PowerMonitorReadings(powerMonitorsArray, 315 resultData.getLongArray(IPowerStatsService.KEY_ENERGY), 316 resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)); 317 if (executor != null) { 318 executor.execute(() -> onResult.onResult(result)); 319 } else { 320 onResult.onResult(result); 321 } 322 } else { 323 RuntimeException error; 324 if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { 325 error = new IllegalArgumentException("Unsupported power monitor"); 326 } else { 327 error = new IllegalStateException( 328 "Unrecognized result code " + resultCode); 329 } 330 if (executor != null) { 331 executor.execute(() -> onResult.onError(error)); 332 } else { 333 onResult.onError(error); 334 } 335 } 336 } 337 }); 338 } catch (RemoteException e) { 339 throw e.rethrowFromSystemServer(); 340 } 341 } 342 } 343