blob: 236797b575567913fedeb3658278af651f1bf307 [file] [log] [blame]
Adam Lesinskib3a1bad2017-05-26 11:50:40 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16package com.android.server.am;
17
Ben Lina8c34e12018-07-20 16:35:39 -070018import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
19
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070020import android.annotation.Nullable;
21import android.bluetooth.BluetoothActivityEnergyInfo;
22import android.bluetooth.BluetoothAdapter;
23import android.content.Context;
24import android.net.wifi.IWifiManager;
25import android.net.wifi.WifiActivityEnergyInfo;
26import android.os.BatteryStats;
27import android.os.Parcelable;
Olivier Gaillardd448d702018-11-20 11:10:08 +000028import android.os.Process;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070029import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.SynchronousResultReceiver;
32import android.os.SystemClock;
Olivier Gaillardd448d702018-11-20 11:10:08 +000033import android.os.ThreadLocalWorkSource;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070034import android.telephony.ModemActivityInfo;
35import android.telephony.TelephonyManager;
36import android.util.IntArray;
37import android.util.Slog;
Bookatz3c648862018-05-25 13:32:43 -070038import android.util.StatsLog;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070039import android.util.TimeUtils;
40
41import com.android.internal.annotations.GuardedBy;
42import com.android.internal.os.BatteryStatsImpl;
Sudheer Shankae544d162017-12-28 17:06:20 -080043import com.android.internal.util.function.pooled.PooledLambda;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070044
45import libcore.util.EmptyArray;
46
Adam Lesinskicf0b2b62017-08-29 19:30:31 -070047import java.util.concurrent.CompletableFuture;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070048import java.util.concurrent.Executors;
49import java.util.concurrent.Future;
Sudheer Shankac57729a2018-02-09 15:44:42 -080050import java.util.concurrent.ScheduledExecutorService;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070051import java.util.concurrent.ThreadFactory;
Sudheer Shankac57729a2018-02-09 15:44:42 -080052import java.util.concurrent.TimeUnit;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070053import java.util.concurrent.TimeoutException;
54
55/**
56 * A Worker that fetches data from external sources (WiFi controller, bluetooth chipset) on a
57 * dedicated thread and updates BatteryStatsImpl with that information.
58 *
59 * As much work as possible is done without holding the BatteryStatsImpl lock, and only the
60 * readily available data is pushed into BatteryStatsImpl with the lock held.
61 */
62class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
63 private static final String TAG = "BatteryExternalStatsWorker";
64 private static final boolean DEBUG = false;
65
66 /**
67 * How long to wait on an individual subsystem to return its stats.
68 */
69 private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
70
71 // There is some accuracy error in wifi reports so allow some slop in the results.
72 private static final long MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS = 750;
73
Sudheer Shankac57729a2018-02-09 15:44:42 -080074 private final ScheduledExecutorService mExecutorService =
75 Executors.newSingleThreadScheduledExecutor(
76 (ThreadFactory) r -> {
Olivier Gaillardd448d702018-11-20 11:10:08 +000077 Thread t = new Thread(
78 () -> {
79 ThreadLocalWorkSource.setUid(Process.myUid());
80 r.run();
81 },
82 "batterystats-worker");
Sudheer Shankac57729a2018-02-09 15:44:42 -080083 t.setPriority(Thread.NORM_PRIORITY);
84 return t;
85 });
Adam Lesinskib3a1bad2017-05-26 11:50:40 -070086
87 private final Context mContext;
88 private final BatteryStatsImpl mStats;
89
90 @GuardedBy("this")
91 private int mUpdateFlags = 0;
92
93 @GuardedBy("this")
94 private Future<?> mCurrentFuture = null;
95
96 @GuardedBy("this")
97 private String mCurrentReason = null;
98
99 @GuardedBy("this")
Sudheer Shankac57729a2018-02-09 15:44:42 -0800100 private boolean mOnBattery;
101
102 @GuardedBy("this")
103 private boolean mOnBatteryScreenOff;
104
105 @GuardedBy("this")
106 private boolean mUseLatestStates = true;
107
108 @GuardedBy("this")
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700109 private final IntArray mUidsToRemove = new IntArray();
110
Sudheer Shankac57729a2018-02-09 15:44:42 -0800111 @GuardedBy("this")
112 private Future<?> mWakelockChangesUpdate;
113
Sudheer Shanka0719c6a2018-04-24 11:12:11 -0700114 @GuardedBy("this")
115 private Future<?> mBatteryLevelSync;
116
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700117 private final Object mWorkerLock = new Object();
118
119 @GuardedBy("mWorkerLock")
120 private IWifiManager mWifiManager = null;
121
122 @GuardedBy("mWorkerLock")
123 private TelephonyManager mTelephony = null;
124
125 // WiFi keeps an accumulated total of stats, unlike Bluetooth.
126 // Keep the last WiFi stats so we can compute a delta.
127 @GuardedBy("mWorkerLock")
128 private WifiActivityEnergyInfo mLastInfo =
Siddharth Rayb50a6842017-12-14 15:15:28 -0800129 new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700130
Sudheer Shankae56013a2018-04-23 11:22:15 -0700131 /**
132 * Timestamp at which all external stats were last collected in
133 * {@link SystemClock#elapsedRealtime()} time base.
134 */
135 @GuardedBy("this")
136 private long mLastCollectionTimeStamp;
137
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700138 BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
139 mContext = context;
140 mStats = stats;
141 }
142
143 @Override
144 public synchronized Future<?> scheduleSync(String reason, int flags) {
145 return scheduleSyncLocked(reason, flags);
146 }
147
148 @Override
149 public synchronized Future<?> scheduleCpuSyncDueToRemovedUid(int uid) {
150 mUidsToRemove.add(uid);
151 return scheduleSyncLocked("remove-uid", UPDATE_CPU);
152 }
153
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800154 @Override
Sudheer Shanka5c19b892018-01-05 17:25:46 -0800155 public synchronized Future<?> scheduleCpuSyncDueToSettingChange() {
156 return scheduleSyncLocked("setting-change", UPDATE_CPU);
157 }
158
159 @Override
Sudheer Shankac20379e2018-02-15 00:06:21 -0800160 public Future<?> scheduleReadProcStateCpuTimes(
161 boolean onBattery, boolean onBatteryScreenOff, long delayMillis) {
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800162 synchronized (mStats) {
Sudheer Shanka5c19b892018-01-05 17:25:46 -0800163 if (!mStats.trackPerProcStateCpuTimes()) {
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800164 return null;
165 }
166 }
167 synchronized (BatteryExternalStatsWorker.this) {
168 if (!mExecutorService.isShutdown()) {
Sudheer Shankac20379e2018-02-15 00:06:21 -0800169 return mExecutorService.schedule(PooledLambda.obtainRunnable(
Sudheer Shankae544d162017-12-28 17:06:20 -0800170 BatteryStatsImpl::updateProcStateCpuTimes,
Sudheer Shankac20379e2018-02-15 00:06:21 -0800171 mStats, onBattery, onBatteryScreenOff).recycleOnUse(),
172 delayMillis, TimeUnit.MILLISECONDS);
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800173 }
174 }
175 return null;
176 }
177
178 @Override
Sudheer Shankae544d162017-12-28 17:06:20 -0800179 public Future<?> scheduleCopyFromAllUidsCpuTimes(
180 boolean onBattery, boolean onBatteryScreenOff) {
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800181 synchronized (mStats) {
Sudheer Shanka5c19b892018-01-05 17:25:46 -0800182 if (!mStats.trackPerProcStateCpuTimes()) {
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800183 return null;
184 }
185 }
186 synchronized (BatteryExternalStatsWorker.this) {
187 if (!mExecutorService.isShutdown()) {
Sudheer Shankae544d162017-12-28 17:06:20 -0800188 return mExecutorService.submit(PooledLambda.obtainRunnable(
189 BatteryStatsImpl::copyFromAllUidsCpuTimes,
190 mStats, onBattery, onBatteryScreenOff).recycleOnUse());
Sudheer Shankab2f83c12017-11-13 19:25:01 -0800191 }
192 }
193 return null;
194 }
195
Sudheer Shankac57729a2018-02-09 15:44:42 -0800196 @Override
197 public Future<?> scheduleCpuSyncDueToScreenStateChange(
198 boolean onBattery, boolean onBatteryScreenOff) {
199 synchronized (BatteryExternalStatsWorker.this) {
200 if (mCurrentFuture == null || (mUpdateFlags & UPDATE_CPU) == 0) {
201 mOnBattery = onBattery;
202 mOnBatteryScreenOff = onBatteryScreenOff;
203 mUseLatestStates = false;
204 }
205 return scheduleSyncLocked("screen-state", UPDATE_CPU);
206 }
207 }
208
209 @Override
210 public Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis) {
Sudheer Shanka0719c6a2018-04-24 11:12:11 -0700211 synchronized (BatteryExternalStatsWorker.this) {
212 mWakelockChangesUpdate = scheduleDelayedSyncLocked(mWakelockChangesUpdate,
213 () -> {
214 scheduleSync("wakelock-change", UPDATE_CPU);
215 scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg());
216 },
217 delayMillis);
218 return mWakelockChangesUpdate;
Sudheer Shankac57729a2018-02-09 15:44:42 -0800219 }
Sudheer Shankac57729a2018-02-09 15:44:42 -0800220 }
221
222 @Override
223 public void cancelCpuSyncDueToWakelockChange() {
Sudheer Shanka0719c6a2018-04-24 11:12:11 -0700224 synchronized (BatteryExternalStatsWorker.this) {
225 if (mWakelockChangesUpdate != null) {
226 mWakelockChangesUpdate.cancel(false);
227 mWakelockChangesUpdate = null;
228 }
Sudheer Shankac57729a2018-02-09 15:44:42 -0800229 }
230 }
231
Sudheer Shanka0719c6a2018-04-24 11:12:11 -0700232 @Override
233 public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
234 synchronized (BatteryExternalStatsWorker.this) {
235 mBatteryLevelSync = scheduleDelayedSyncLocked(mBatteryLevelSync,
236 () -> scheduleSync("battery-level", UPDATE_ALL),
237 delayMillis);
238 return mBatteryLevelSync;
239 }
240 }
241
242 @GuardedBy("this")
243 private void cancelSyncDueToBatteryLevelChangeLocked() {
244 if (mBatteryLevelSync != null) {
245 mBatteryLevelSync.cancel(false);
246 mBatteryLevelSync = null;
247 }
248 }
249
250 /**
251 * Schedule a sync {@param syncRunnable} with a delay. If there's already a scheduled sync, a
252 * new sync won't be scheduled unless it is being scheduled to run immediately (delayMillis=0).
253 *
254 * @param lastScheduledSync the task which was earlier scheduled to run
255 * @param syncRunnable the task that needs to be scheduled to run
256 * @param delayMillis time after which {@param syncRunnable} needs to be scheduled
257 * @return scheduled {@link Future} which can be used to check if task is completed or to
258 * cancel it if needed
259 */
260 @GuardedBy("this")
261 private Future<?> scheduleDelayedSyncLocked(Future<?> lastScheduledSync, Runnable syncRunnable,
262 long delayMillis) {
263 if (mExecutorService.isShutdown()) {
264 return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
265 }
266
267 if (lastScheduledSync != null) {
268 // If there's already a scheduled task, leave it as is if we're trying to
269 // re-schedule it again with a delay, otherwise cancel and re-schedule it.
270 if (delayMillis == 0) {
271 lastScheduledSync.cancel(false);
272 } else {
273 return lastScheduledSync;
274 }
275 }
276
277 return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS);
278 }
279
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700280 public synchronized Future<?> scheduleWrite() {
Adam Lesinskicf0b2b62017-08-29 19:30:31 -0700281 if (mExecutorService.isShutdown()) {
282 return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
283 }
284
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700285 scheduleSyncLocked("write", UPDATE_ALL);
286 // Since we use a single threaded executor, we can assume the next scheduled task's
287 // Future finishes after the sync.
288 return mExecutorService.submit(mWriteTask);
289 }
290
291 /**
292 * Schedules a task to run on the BatteryExternalStatsWorker thread. If scheduling more work
293 * within the task, never wait on the resulting Future. This will result in a deadlock.
294 */
295 public synchronized void scheduleRunnable(Runnable runnable) {
Adam Lesinskicf0b2b62017-08-29 19:30:31 -0700296 if (!mExecutorService.isShutdown()) {
297 mExecutorService.submit(runnable);
298 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700299 }
300
301 public void shutdown() {
302 mExecutorService.shutdownNow();
303 }
304
Andreas Gampea36dc622018-02-05 17:19:22 -0800305 @GuardedBy("this")
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700306 private Future<?> scheduleSyncLocked(String reason, int flags) {
Adam Lesinskicf0b2b62017-08-29 19:30:31 -0700307 if (mExecutorService.isShutdown()) {
308 return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
309 }
310
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700311 if (mCurrentFuture == null) {
312 mUpdateFlags = flags;
313 mCurrentReason = reason;
314 mCurrentFuture = mExecutorService.submit(mSyncTask);
315 }
316 mUpdateFlags |= flags;
317 return mCurrentFuture;
318 }
319
Sudheer Shankae56013a2018-04-23 11:22:15 -0700320 long getLastCollectionTimeStamp() {
321 synchronized (this) {
322 return mLastCollectionTimeStamp;
323 }
324 }
325
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700326 private final Runnable mSyncTask = new Runnable() {
327 @Override
328 public void run() {
329 // Capture a snapshot of the state we are meant to process.
330 final int updateFlags;
331 final String reason;
332 final int[] uidsToRemove;
Sudheer Shankac57729a2018-02-09 15:44:42 -0800333 final boolean onBattery;
334 final boolean onBatteryScreenOff;
335 final boolean useLatestStates;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700336 synchronized (BatteryExternalStatsWorker.this) {
337 updateFlags = mUpdateFlags;
338 reason = mCurrentReason;
339 uidsToRemove = mUidsToRemove.size() > 0 ? mUidsToRemove.toArray() : EmptyArray.INT;
Sudheer Shankac57729a2018-02-09 15:44:42 -0800340 onBattery = mOnBattery;
341 onBatteryScreenOff = mOnBatteryScreenOff;
342 useLatestStates = mUseLatestStates;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700343 mUpdateFlags = 0;
344 mCurrentReason = null;
345 mUidsToRemove.clear();
346 mCurrentFuture = null;
Sudheer Shankac57729a2018-02-09 15:44:42 -0800347 mUseLatestStates = true;
Sudheer Shanka0719c6a2018-04-24 11:12:11 -0700348 if ((updateFlags & UPDATE_ALL) != 0) {
349 cancelSyncDueToBatteryLevelChangeLocked();
350 }
351 if ((updateFlags & UPDATE_CPU) != 0) {
352 cancelCpuSyncDueToWakelockChange();
353 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700354 }
355
Mike Ma561a8d92018-03-20 18:24:05 -0700356 try {
357 synchronized (mWorkerLock) {
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700358 if (DEBUG) {
Mike Ma561a8d92018-03-20 18:24:05 -0700359 Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
360 }
361 try {
362 updateExternalStatsLocked(reason, updateFlags, onBattery,
363 onBatteryScreenOff, useLatestStates);
364 } finally {
365 if (DEBUG) {
366 Slog.d(TAG, "end updateExternalStatsSync");
367 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700368 }
369 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700370
Mike Ma561a8d92018-03-20 18:24:05 -0700371 if ((updateFlags & UPDATE_CPU) != 0) {
372 mStats.copyFromAllUidsCpuTimes();
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700373 }
Mike Ma561a8d92018-03-20 18:24:05 -0700374
375 // Clean up any UIDs if necessary.
376 synchronized (mStats) {
377 for (int uid : uidsToRemove) {
Bookatz3c648862018-05-25 13:32:43 -0700378 StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, -1, uid,
379 StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
Mike Ma561a8d92018-03-20 18:24:05 -0700380 mStats.removeIsolatedUidLocked(uid);
381 }
382 mStats.clearPendingRemovedUids();
383 }
384 } catch (Exception e) {
385 Slog.wtf(TAG, "Error updating external stats: ", e);
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700386 }
Sudheer Shankae56013a2018-04-23 11:22:15 -0700387
388 synchronized (BatteryExternalStatsWorker.this) {
389 mLastCollectionTimeStamp = SystemClock.elapsedRealtime();
390 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700391 }
392 };
393
394 private final Runnable mWriteTask = new Runnable() {
395 @Override
396 public void run() {
397 synchronized (mStats) {
398 mStats.writeAsyncLocked();
399 }
400 }
401 };
402
Andreas Gampea36dc622018-02-05 17:19:22 -0800403 @GuardedBy("mWorkerLock")
Sudheer Shankac57729a2018-02-09 15:44:42 -0800404 private void updateExternalStatsLocked(final String reason, int updateFlags,
405 boolean onBattery, boolean onBatteryScreenOff, boolean useLatestStates) {
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700406 // We will request data from external processes asynchronously, and wait on a timeout.
407 SynchronousResultReceiver wifiReceiver = null;
408 SynchronousResultReceiver bluetoothReceiver = null;
409 SynchronousResultReceiver modemReceiver = null;
410
411 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
412 // We were asked to fetch WiFi data.
413 if (mWifiManager == null) {
414 mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService(
415 Context.WIFI_SERVICE));
416 }
417
418 if (mWifiManager != null) {
419 try {
Ben Lina8c34e12018-07-20 16:35:39 -0700420 // Only fetch WiFi power data if it is supported.
421 if ((mWifiManager.getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0) {
422 wifiReceiver = new SynchronousResultReceiver("wifi");
423 mWifiManager.requestActivityInfo(wifiReceiver);
424 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700425 } catch (RemoteException e) {
426 // Oh well.
427 }
428 }
429 }
430
431 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
432 // We were asked to fetch Bluetooth data.
433 final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
434 if (adapter != null) {
435 bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
436 adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
437 }
438 }
439
440 if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
441 // We were asked to fetch Telephony data.
442 if (mTelephony == null) {
443 mTelephony = TelephonyManager.from(mContext);
444 }
445
446 if (mTelephony != null) {
447 modemReceiver = new SynchronousResultReceiver("telephony");
448 mTelephony.requestModemActivityInfo(modemReceiver);
449 }
450 }
451
452 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
453 final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
454 final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
455
456 synchronized (mStats) {
457 mStats.addHistoryEventLocked(
458 SystemClock.elapsedRealtime(),
459 SystemClock.uptimeMillis(),
460 BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
461 reason, 0);
462
463 if ((updateFlags & UPDATE_CPU) != 0) {
Sudheer Shankac57729a2018-02-09 15:44:42 -0800464 if (useLatestStates) {
465 onBattery = mStats.isOnBatteryLocked();
466 onBatteryScreenOff = mStats.isOnBatteryScreenOffLocked();
467 }
468 mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff);
469 }
470
471 if ((updateFlags & UPDATE_ALL) != 0) {
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700472 mStats.updateKernelWakelocksLocked();
473 mStats.updateKernelMemoryBandwidthLocked();
474 }
475
Bookatz50df7112017-08-04 14:53:26 -0700476 if ((updateFlags & UPDATE_RPM) != 0) {
477 mStats.updateRpmStatsLocked();
478 }
479
Blake Kragtencb308d92019-01-28 10:54:13 -0800480 if ((updateFlags & UPDATE_RAIL) != 0) {
481 mStats.updateRailStatsLocked();
482 }
483
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700484 if (bluetoothInfo != null) {
485 if (bluetoothInfo.isValid()) {
486 mStats.updateBluetoothStateLocked(bluetoothInfo);
487 } else {
Mike Ma561a8d92018-03-20 18:24:05 -0700488 Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700489 }
490 }
491 }
492
493 // WiFi and Modem state are updated without the mStats lock held, because they
494 // do some network stats retrieval before internally grabbing the mStats lock.
495
496 if (wifiInfo != null) {
497 if (wifiInfo.isValid()) {
498 mStats.updateWifiState(extractDeltaLocked(wifiInfo));
499 } else {
Mike Ma561a8d92018-03-20 18:24:05 -0700500 Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700501 }
502 }
503
504 if (modemInfo != null) {
505 if (modemInfo.isValid()) {
506 mStats.updateMobileRadioState(modemInfo);
507 } else {
Mike Ma561a8d92018-03-20 18:24:05 -0700508 Slog.w(TAG, "modem info is invalid: " + modemInfo);
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700509 }
510 }
511 }
512
513 /**
514 * Helper method to extract the Parcelable controller info from a
515 * SynchronousResultReceiver.
516 */
517 private static <T extends Parcelable> T awaitControllerInfo(
518 @Nullable SynchronousResultReceiver receiver) {
519 if (receiver == null) {
520 return null;
521 }
522
523 try {
524 final SynchronousResultReceiver.Result result =
525 receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
526 if (result.bundle != null) {
527 // This is the final destination for the Bundle.
528 result.bundle.setDefusable(true);
529
530 final T data = result.bundle.getParcelable(
531 BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
532 if (data != null) {
533 return data;
534 }
535 }
536 Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
537 } catch (TimeoutException e) {
538 Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
539 }
540 return null;
541 }
542
Andreas Gampea36dc622018-02-05 17:19:22 -0800543 @GuardedBy("mWorkerLock")
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700544 private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
545 final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
Siddharth Rayb50a6842017-12-14 15:15:28 -0800546 final long lastScanMs = mLastInfo.mControllerScanTimeMs;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700547 final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
548 final long lastTxMs = mLastInfo.mControllerTxTimeMs;
549 final long lastRxMs = mLastInfo.mControllerRxTimeMs;
550 final long lastEnergy = mLastInfo.mControllerEnergyUsed;
551
552 // We will modify the last info object to be the delta, and store the new
553 // WifiActivityEnergyInfo object as our last one.
554 final WifiActivityEnergyInfo delta = mLastInfo;
555 delta.mTimestamp = latest.getTimeStamp();
556 delta.mStackState = latest.getStackState();
557
558 final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs;
559 final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
560 final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
Siddharth Rayb50a6842017-12-14 15:15:28 -0800561 final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700562
Siddharth Ray97f62bf2018-06-17 18:28:44 -0700563 if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700564 // The stats were reset by the WiFi system (which is why our delta is negative).
Siddharth Ray97f62bf2018-06-17 18:28:44 -0700565 // Returns the unaltered stats. The total on time should not exceed the time
566 // duartion between reports.
567 final long totalOnTimeMs = latest.mControllerTxTimeMs + latest.mControllerRxTimeMs
568 + latest.mControllerIdleTimeMs;
569 if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
570 delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
571 delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
572 delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
573 delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
574 delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
575 } else {
576 delta.mControllerEnergyUsed = 0;
577 delta.mControllerRxTimeMs = 0;
578 delta.mControllerTxTimeMs = 0;
579 delta.mControllerIdleTimeMs = 0;
580 delta.mControllerScanTimeMs = 0;
581 }
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700582 Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
583 } else {
584 final long totalActiveTimeMs = txTimeMs + rxTimeMs;
585 long maxExpectedIdleTimeMs;
586 if (totalActiveTimeMs > timePeriodMs) {
587 // Cap the max idle time at zero since the active time consumed the whole time
588 maxExpectedIdleTimeMs = 0;
589 if (totalActiveTimeMs > timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
590 StringBuilder sb = new StringBuilder();
591 sb.append("Total Active time ");
592 TimeUtils.formatDuration(totalActiveTimeMs, sb);
593 sb.append(" is longer than sample period ");
594 TimeUtils.formatDuration(timePeriodMs, sb);
595 sb.append(".\n");
596 sb.append("Previous WiFi snapshot: ").append("idle=");
597 TimeUtils.formatDuration(lastIdleMs, sb);
598 sb.append(" rx=");
599 TimeUtils.formatDuration(lastRxMs, sb);
600 sb.append(" tx=");
601 TimeUtils.formatDuration(lastTxMs, sb);
602 sb.append(" e=").append(lastEnergy);
603 sb.append("\n");
604 sb.append("Current WiFi snapshot: ").append("idle=");
605 TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
606 sb.append(" rx=");
607 TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
608 sb.append(" tx=");
609 TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
610 sb.append(" e=").append(latest.mControllerEnergyUsed);
611 Slog.wtf(TAG, sb.toString());
612 }
613 } else {
614 maxExpectedIdleTimeMs = timePeriodMs - totalActiveTimeMs;
615 }
616 // These times seem to be the most reliable.
617 delta.mControllerTxTimeMs = txTimeMs;
618 delta.mControllerRxTimeMs = rxTimeMs;
Siddharth Rayb50a6842017-12-14 15:15:28 -0800619 delta.mControllerScanTimeMs = scanTimeMs;
Adam Lesinskib3a1bad2017-05-26 11:50:40 -0700620 // WiFi calculates the idle time as a difference from the on time and the various
621 // Rx + Tx times. There seems to be some missing time there because this sometimes
622 // becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
623 // time from the difference in timestamps.
624 // b/21613534
625 delta.mControllerIdleTimeMs = Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs));
626 delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
627 }
628
629 mLastInfo = latest;
630 return delta;
631 }
632}