Merge "BatteryStats: Introduce Async external stats requests" into nyc-dev

am: 0d47f4b

* commit '0d47f4b6463077074abd6bed489a5f9243615c64':
  BatteryStats: Introduce Async external stats requests

Change-Id: I5fbb54546528942e36293e1fbcbb03290a037823
This commit is contained in:
Adam Lesinski
2016-04-18 23:38:08 +00:00
committed by android-build-merger
11 changed files with 427 additions and 208 deletions

View File

@@ -156,8 +156,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable {
* @return if the record is valid * @return if the record is valid
*/ */
public boolean isValid() { public boolean isValid() {
return ((mControllerTxTimeMs !=0) || return ((mControllerTxTimeMs >=0) &&
(mControllerRxTimeMs !=0) || (mControllerRxTimeMs >=0) &&
(mControllerIdleTimeMs !=0)); (mControllerIdleTimeMs >=0));
} }
} }

View File

@@ -31,11 +31,14 @@ import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ScanSettings;
import android.content.Context; import android.content.Context;
import android.os.BatteryStats;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SynchronousResultReceiver;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@@ -53,6 +56,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeoutException;
/** /**
* Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
@@ -1369,33 +1373,62 @@ public final class BluetoothAdapter {
* *
* @return a record with {@link BluetoothActivityEnergyInfo} or null if * @return a record with {@link BluetoothActivityEnergyInfo} or null if
* report is unavailable or unsupported * report is unavailable or unsupported
* @deprecated use the asynchronous
* {@link #requestControllerActivityEnergyInfo(int, ResultReceiver)} instead.
* @hide * @hide
*/ */
@Deprecated
public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
if (getState() != STATE_ON) return null; SynchronousResultReceiver receiver = new SynchronousResultReceiver();
requestControllerActivityEnergyInfo(updateType, receiver);
try {
SynchronousResultReceiver.Result result = receiver.awaitResult(1000);
if (result.bundle != null) {
return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
}
} catch (TimeoutException e) {
Log.e(TAG, "getControllerActivityEnergyInfo timed out");
}
return null;
}
/**
* Request the record of {@link BluetoothActivityEnergyInfo} object that
* has the activity and energy info. This can be used to ascertain what
* the controller has been up to, since the last sample.
*
* A null value for the activity info object may be sent if the bluetooth service is
* unreachable or the device does not support reporting such information.
*
* @param updateType Type of info, cached vs refreshed.
* @param result The callback to which to send the activity info.
* @hide
*/
public void requestControllerActivityEnergyInfo(int updateType, ResultReceiver result) {
if (getState() != STATE_ON) {
result.send(0, null);
return;
}
try { try {
BluetoothActivityEnergyInfo record;
if (!mService.isActivityAndEnergyReportingSupported()) { if (!mService.isActivityAndEnergyReportingSupported()) {
return null; result.send(0, null);
return;
} }
synchronized(this) { synchronized(this) {
if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) { if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) {
mService.getActivityEnergyInfoFromController(); mService.getActivityEnergyInfoFromController();
wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS); wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS);
} }
record = mService.reportActivityInfo(); mService.requestActivityInfo(result);
if (record.isValid()) {
return record;
} else {
return null;
}
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e); Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e);
result.send(0, null);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
result.send(0, null);
} }
return null;
} }
/** /**

View File

@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.OobData; import android.bluetooth.OobData;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
/** /**
* System private API for talking with the Bluetooth service. * System private API for talking with the Bluetooth service.
@@ -104,6 +105,15 @@ interface IBluetooth
void getActivityEnergyInfoFromController(); void getActivityEnergyInfoFromController();
BluetoothActivityEnergyInfo reportActivityInfo(); BluetoothActivityEnergyInfo reportActivityInfo();
/**
* Requests the controller activity info asynchronously.
* The implementor is expected to reply with the
* {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the
* key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}.
* The result code is ignored.
*/
oneway void requestActivityInfo(in ResultReceiver result);
void onLeServiceUp(); void onLeServiceUp();
void onBrEdrDown(); void onBrEdrDown();
} }

View File

@@ -242,6 +242,8 @@ public abstract class BatteryStats implements Parcelable {
private static final String VIDEO_DATA = "vid"; private static final String VIDEO_DATA = "vid";
private static final String AUDIO_DATA = "aud"; private static final String AUDIO_DATA = "aud";
public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
private final StringBuilder mFormatBuilder = new StringBuilder(32); private final StringBuilder mFormatBuilder = new StringBuilder(32);
private final Formatter mFormatter = new Formatter(mFormatBuilder); private final Formatter mFormatter = new Formatter(mFormatBuilder);

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Extends ResultReceiver to allow the server end of the ResultReceiver to synchronously wait
* on the response from the client. This enables an RPC like system but with the ability to
* timeout and discard late results.
*
* NOTE: Can only be used for one response. Subsequent responses on the same instance are ignored.
* {@hide}
*/
public class SynchronousResultReceiver extends ResultReceiver {
public static class Result {
public int resultCode;
@Nullable public Bundle bundle;
public Result(int resultCode, @Nullable Bundle bundle) {
this.resultCode = resultCode;
this.bundle = bundle;
}
}
private final CompletableFuture<Result> mFuture = new CompletableFuture<>();
public SynchronousResultReceiver() {
super((Handler) null);
}
@Override
final protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
mFuture.complete(new Result(resultCode, resultData));
}
/**
* Blocks waiting for the result from the remote client.
*
* @return the Result
* @throws TimeoutException if the timeout in milliseconds expired.
*/
public @NonNull Result awaitResult(long timeoutMillis) throws TimeoutException {
final long deadline = System.currentTimeMillis() + timeoutMillis;
while (timeoutMillis >= 0) {
try {
return mFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
// This will NEVER happen.
throw new AssertionError("Error receiving response", e);
} catch (InterruptedException e) {
// The thread was interrupted, try and get the value again, this time
// with the remaining time until the deadline.
timeoutMillis -= deadline - System.currentTimeMillis();
}
}
throw new TimeoutException();
}
}

View File

@@ -18,10 +18,13 @@ package com.android.internal.app;
import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.BatteryStatsImpl;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.net.wifi.WifiActivityEnergyInfo;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.WorkSource; import android.os.WorkSource;
import android.os.health.HealthStatsParceler; import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo; import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength; import android.telephony.SignalStrength;
interface IBatteryStats { interface IBatteryStats {
@@ -129,4 +132,8 @@ interface IBatteryStats {
HealthStatsParceler takeUidSnapshot(int uid); HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid); HealthStatsParceler[] takeUidSnapshots(in int[] uid);
oneway void noteBluetoothControllerActivity(in BluetoothActivityEnergyInfo info);
oneway void noteModemControllerActivity(in ModemActivityInfo info);
oneway void noteWifiControllerActivity(in WifiActivityEnergyInfo info);
} }

View File

@@ -16,6 +16,7 @@
package com.android.server.am; package com.android.server.am;
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.Context; import android.content.Context;
@@ -32,11 +33,12 @@ import android.os.Message;
import android.os.Parcel; import android.os.Parcel;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.ParcelFormatException; import android.os.ParcelFormatException;
import android.os.PowerManager; import android.os.Parcelable;
import android.os.PowerManagerInternal; import android.os.PowerManagerInternal;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.WorkSource; import android.os.WorkSource;
@@ -56,7 +58,6 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerProfile;
import com.android.internal.telephony.ITelephony;
import com.android.server.FgThread; import com.android.server.FgThread;
import com.android.server.LocalServices; import com.android.server.LocalServices;
@@ -71,7 +72,7 @@ import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.concurrent.TimeoutException;
/** /**
* All information we are collecting about things that can happen that impact * All information we are collecting about things that can happen that impact
@@ -81,15 +82,32 @@ public final class BatteryStatsService extends IBatteryStats.Stub
implements PowerManagerInternal.LowPowerModeListener { implements PowerManagerInternal.LowPowerModeListener {
static final String TAG = "BatteryStatsService"; static final String TAG = "BatteryStatsService";
static IBatteryStats sService; /**
* How long to wait on an individual subsystem to return its stats.
*/
private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
private static IBatteryStats sService;
final BatteryStatsImpl mStats; final BatteryStatsImpl mStats;
final BatteryStatsHandler mHandler; private final BatteryStatsHandler mHandler;
Context mContext; private Context mContext;
PowerManagerInternal mPowerManagerInternal; private IWifiManager mWifiManager;
private TelephonyManager mTelephony;
// Lock acquired when extracting data from external sources.
private final Object mExternalStatsLock = new Object();
// WiFi keeps an accumulated total of stats, unlike Bluetooth.
// Keep the last WiFi stats so we can compute a delta.
@GuardedBy("mExternalStatsLock")
private WifiActivityEnergyInfo mLastInfo =
new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync { class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
public static final int MSG_SYNC_EXTERNAL_STATS = 1; public static final int MSG_SYNC_EXTERNAL_STATS = 1;
public static final int MSG_WRITE_TO_DISK = 2; public static final int MSG_WRITE_TO_DISK = 2;
private int mUpdateFlags = 0; private int mUpdateFlags = 0;
private IntArray mUidsToRemove = new IntArray(); private IntArray mUidsToRemove = new IntArray();
@@ -107,7 +125,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
updateFlags = mUpdateFlags; updateFlags = mUpdateFlags;
mUpdateFlags = 0; mUpdateFlags = 0;
} }
updateExternalStats((String)msg.obj, updateFlags); updateExternalStatsSync((String)msg.obj, updateFlags);
// other parts of the system could be calling into us // other parts of the system could be calling into us
// from mStats in order to report of changes. We must grab the mStats // from mStats in order to report of changes. We must grab the mStats
@@ -124,7 +142,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
break; break;
case MSG_WRITE_TO_DISK: case MSG_WRITE_TO_DISK:
updateExternalStats("write", UPDATE_ALL); updateExternalStatsSync("write", UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
mStats.writeAsyncLocked(); mStats.writeAsyncLocked();
} }
@@ -178,16 +196,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
* initialized. So we initialize the low power observer later. * initialized. So we initialize the low power observer later.
*/ */
public void initPowerManagement() { public void initPowerManagement() {
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(this); powerMgr.registerLowPowerModeObserver(this);
mStats.notePowerSaveMode(mPowerManagerInternal.getLowPowerModeEnabled()); mStats.notePowerSaveMode(powerMgr.getLowPowerModeEnabled());
(new WakeupReasonThread()).start(); (new WakeupReasonThread()).start();
} }
public void shutdown() { public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown..."); Slog.w("BatteryStats", "Writing battery stats before shutdown...");
updateExternalStats("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
mStats.shutdownLocked(); mStats.shutdownLocked();
} }
@@ -287,7 +305,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
//Slog.i("foo", "SENDING BATTERY INFO:"); //Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
Parcel out = Parcel.obtain(); Parcel out = Parcel.obtain();
updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
mStats.writeToParcel(out, 0); mStats.writeToParcel(out, 0);
} }
@@ -302,7 +320,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
//Slog.i("foo", "SENDING BATTERY INFO:"); //Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
Parcel out = Parcel.obtain(); Parcel out = Parcel.obtain();
updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
mStats.writeToParcel(out, 0); mStats.writeToParcel(out, 0);
} }
@@ -875,6 +893,47 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} }
} }
@Override
public void noteWifiControllerActivity(WifiActivityEnergyInfo info) {
enforceCallingPermission();
if (info == null || !info.isValid()) {
Slog.e(TAG, "invalid wifi data given: " + info);
return;
}
synchronized (mStats) {
mStats.updateWifiStateLocked(info);
}
}
@Override
public void noteBluetoothControllerActivity(BluetoothActivityEnergyInfo info) {
enforceCallingPermission();
if (info == null || !info.isValid()) {
Slog.e(TAG, "invalid bluetooth data given: " + info);
return;
}
synchronized (mStats) {
mStats.updateBluetoothStateLocked(info);
}
}
@Override
public void noteModemControllerActivity(ModemActivityInfo info) {
enforceCallingPermission();
if (info == null || !info.isValid()) {
Slog.e(TAG, "invalid modem data given: " + info);
return;
}
synchronized (mStats) {
mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(), info);
}
}
public boolean isOnBattery() { public boolean isOnBattery() {
return mStats.isOnBattery(); return mStats.isOnBattery();
} }
@@ -901,7 +960,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// Sync external stats first as the battery has changed states. If we don't sync // Sync external stats first as the battery has changed states. If we don't sync
// immediately here, we may not collect the relevant data later. // immediately here, we may not collect the relevant data later.
updateExternalStats("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt); mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
} }
@@ -1088,9 +1147,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
pw.println("Battery stats reset."); pw.println("Battery stats reset.");
noOutput = true; noOutput = true;
} }
updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
} else if ("--write".equals(arg)) { } else if ("--write".equals(arg)) {
updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
mStats.writeSyncLocked(); mStats.writeSyncLocked();
pw.println("Battery stats written."); pw.println("Battery stats written.");
@@ -1154,7 +1213,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY; flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
} }
// Fetch data from external sources and update the BatteryStatsImpl object with them. // Fetch data from external sources and update the BatteryStatsImpl object with them.
updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
@@ -1215,145 +1274,96 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} }
} }
// Objects for extracting data from external sources. private WifiActivityEnergyInfo extractDelta(WifiActivityEnergyInfo latest) {
private final Object mExternalStatsLock = new Object(); final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
final long lastTxMs = mLastInfo.mControllerTxTimeMs;
final long lastRxMs = mLastInfo.mControllerRxTimeMs;
final long lastEnergy = mLastInfo.mControllerEnergyUsed;
@GuardedBy("mExternalStatsLock") // We will modify the last info object to be the delta, and store the new
private IWifiManager mWifiManager; // WifiActivityEnergyInfo object as our last one.
final WifiActivityEnergyInfo delta = mLastInfo;
delta.mTimestamp = latest.getTimeStamp();
delta.mStackState = latest.getStackState();
// WiFi keeps an accumulated total of stats, unlike Bluetooth. // These times seem to be the most reliable.
// Keep the last WiFi stats so we can compute a delta. delta.mControllerTxTimeMs = latest.mControllerTxTimeMs - lastTxMs;
@GuardedBy("mExternalStatsLock") delta.mControllerRxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
private WifiActivityEnergyInfo mLastInfo =
new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
@GuardedBy("mExternalStatsLock") // WiFi calculates the idle time as a difference from the on time and the various
private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() { // Rx + Tx times. There seems to be some missing time there because this sometimes
if (mWifiManager == null) { // becomes negative. Just cap it at 0 and move on.
mWifiManager = IWifiManager.Stub.asInterface( // b/21613534
ServiceManager.getService(Context.WIFI_SERVICE)); delta.mControllerIdleTimeMs = Math.max(0, latest.mControllerIdleTimeMs - lastIdleMs);
if (mWifiManager == null) { delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
return null;
} if (delta.mControllerTxTimeMs < 0 || delta.mControllerRxTimeMs < 0) {
// The stats were reset by the WiFi system (which is why our delta is negative).
// Returns the unaltered stats.
delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
} }
try { // There is some accuracy error in reports so allow some slop in the results.
// We read the data even if we are not on battery. This is so that we keep the final long SAMPLE_ERROR_MILLIS = 750;
// correct delta from when we should start reading (aka when we are on battery). final long totalTimeMs = delta.mControllerIdleTimeMs + delta.mControllerRxTimeMs +
WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo(); delta.mControllerTxTimeMs;
if (info != null && info.isValid()) { if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
if (info.mControllerEnergyUsed < 0 || info.mControllerIdleTimeMs < 0 || StringBuilder sb = new StringBuilder();
info.mControllerRxTimeMs < 0 || info.mControllerTxTimeMs < 0) { sb.append("Total time ");
Slog.wtf(TAG, "Reported WiFi energy data is invalid: " + info); TimeUtils.formatDuration(totalTimeMs, sb);
return null; sb.append(" is longer than sample period ");
} TimeUtils.formatDuration(timePeriodMs, sb);
sb.append(".\n");
final long timePeriodMs = info.mTimestamp - mLastInfo.mTimestamp; sb.append("Previous WiFi snapshot: ").append("idle=");
final long lastIdleMs = mLastInfo.mControllerIdleTimeMs; TimeUtils.formatDuration(lastIdleMs, sb);
final long lastTxMs = mLastInfo.mControllerTxTimeMs; sb.append(" rx=");
final long lastRxMs = mLastInfo.mControllerRxTimeMs; TimeUtils.formatDuration(lastRxMs, sb);
final long lastEnergy = mLastInfo.mControllerEnergyUsed; sb.append(" tx=");
TimeUtils.formatDuration(lastTxMs, sb);
// We will modify the last info object to be the delta, and store the new sb.append(" e=").append(lastEnergy);
// WifiActivityEnergyInfo object as our last one. sb.append("\n");
final WifiActivityEnergyInfo result = mLastInfo; sb.append("Current WiFi snapshot: ").append("idle=");
result.mTimestamp = info.getTimeStamp(); TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
result.mStackState = info.getStackState(); sb.append(" rx=");
TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
// These times seem to be the most reliable. sb.append(" tx=");
result.mControllerTxTimeMs = info.mControllerTxTimeMs - lastTxMs; TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
result.mControllerRxTimeMs = info.mControllerRxTimeMs - lastRxMs; sb.append(" e=").append(latest.mControllerEnergyUsed);
Slog.wtf(TAG, sb.toString());
// WiFi calculates the idle time as a difference from the on time and the various
// Rx + Tx times. There seems to be some missing time there because this sometimes
// becomes negative. Just cap it at 0 and move on.
// b/21613534
result.mControllerIdleTimeMs = Math.max(0, info.mControllerIdleTimeMs - lastIdleMs);
result.mControllerEnergyUsed =
Math.max(0, info.mControllerEnergyUsed - lastEnergy);
if (result.mControllerTxTimeMs < 0 ||
result.mControllerRxTimeMs < 0) {
// The stats were reset by the WiFi system (which is why our delta is negative).
// Returns the unaltered stats.
result.mControllerEnergyUsed = info.mControllerEnergyUsed;
result.mControllerRxTimeMs = info.mControllerRxTimeMs;
result.mControllerTxTimeMs = info.mControllerTxTimeMs;
result.mControllerIdleTimeMs = info.mControllerIdleTimeMs;
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
}
// There is some accuracy error in reports so allow some slop in the results.
final long SAMPLE_ERROR_MILLIS = 750;
final long totalTimeMs = result.mControllerIdleTimeMs + result.mControllerRxTimeMs +
result.mControllerTxTimeMs;
if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
StringBuilder sb = new StringBuilder();
sb.append("Total time ");
TimeUtils.formatDuration(totalTimeMs, sb);
sb.append(" is longer than sample period ");
TimeUtils.formatDuration(timePeriodMs, sb);
sb.append(".\n");
sb.append("Previous WiFi snapshot: ").append("idle=");
TimeUtils.formatDuration(lastIdleMs, sb);
sb.append(" rx=");
TimeUtils.formatDuration(lastRxMs, sb);
sb.append(" tx=");
TimeUtils.formatDuration(lastTxMs, sb);
sb.append(" e=").append(lastEnergy);
sb.append("\n");
sb.append("Current WiFi snapshot: ").append("idle=");
TimeUtils.formatDuration(info.mControllerIdleTimeMs, sb);
sb.append(" rx=");
TimeUtils.formatDuration(info.mControllerRxTimeMs, sb);
sb.append(" tx=");
TimeUtils.formatDuration(info.mControllerTxTimeMs, sb);
sb.append(" e=").append(info.mControllerEnergyUsed);
Slog.wtf(TAG, sb.toString());
}
mLastInfo = info;
return result;
}
} catch (RemoteException e) {
// Nothing to report, WiFi is dead.
} }
return null;
mLastInfo = latest;
return delta;
} }
@GuardedBy("mExternalStatsLock") /**
private BluetoothActivityEnergyInfo pullBluetoothEnergyInfoLocked() { * Helper method to extract the Parcelable controller info from a
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); * SynchronousResultReceiver.
if (adapter != null) { */
BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo( private static <T extends Parcelable> T awaitControllerInfo(
BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED); @Nullable SynchronousResultReceiver receiver) throws TimeoutException {
if (info != null && info.isValid()) { if (receiver == null) {
if (info.getControllerEnergyUsed() < 0 || info.getControllerIdleTimeMillis() < 0 || return null;
info.getControllerRxTimeMillis() < 0 || info.getControllerTxTimeMillis() < 0) {
Slog.wtf(TAG, "Bluetooth energy data is invalid: " + info);
}
return info;
}
} }
return null;
}
@GuardedBy("mExternalStatsLock") final SynchronousResultReceiver.Result result =
private ModemActivityInfo pullModemActivityInfoLocked() { receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
ITelephony tm = ITelephony.Stub.asInterface(ServiceManager.getService( if (result.bundle != null) {
Context.TELEPHONY_SERVICE)); // This is the final destination for the Bundle.
try { result.bundle.setDefusable(true);
if (tm != null) {
ModemActivityInfo info = tm.getModemActivityInfo(); final T data = result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
if (info == null || info.isValid()) { if (data != null) {
return info; return data;
}
Slog.wtf(TAG, "Modem activity info is invalid: " + info);
} }
} catch (RemoteException e) {
// Nothing to do.
} }
Slog.e(TAG, "no controller energy info supplied");
return null; return null;
} }
@@ -1371,58 +1381,108 @@ public final class BatteryStatsService extends IBatteryStats.Stub
* {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_WIFI}, * {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_WIFI},
* and {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_BT}. * and {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_BT}.
*/ */
void updateExternalStats(final String reason, final int updateFlags) { void updateExternalStatsSync(final String reason, int updateFlags) {
SynchronousResultReceiver wifiReceiver = null;
SynchronousResultReceiver bluetoothReceiver = null;
SynchronousResultReceiver modemReceiver = null;
synchronized (mExternalStatsLock) { synchronized (mExternalStatsLock) {
if (mContext == null) { if (mContext == null) {
// We haven't started yet (which means the BatteryStatsImpl object has // Don't do any work yet.
// no power profile. Don't consume data we can't compute yet.
return; return;
} }
if (BatteryStatsImpl.DEBUG_ENERGY) {
Slog.d(TAG, "Updating external stats: reason=" + reason);
}
WifiActivityEnergyInfo wifiEnergyInfo = null;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
wifiEnergyInfo = pullWifiEnergyInfoLocked(); if (mWifiManager == null) {
mWifiManager = IWifiManager.Stub.asInterface(
ServiceManager.getService(Context.WIFI_SERVICE));
}
if (mWifiManager != null) {
try {
wifiReceiver = new SynchronousResultReceiver();
mWifiManager.requestActivityInfo(wifiReceiver);
} catch (RemoteException e) {
// Oh well.
}
}
} }
ModemActivityInfo modemActivityInfo = null;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
modemActivityInfo = pullModemActivityInfoLocked();
}
BluetoothActivityEnergyInfo bluetoothEnergyInfo = null;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) { if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
// We only pull bluetooth stats when we have to, as we are not distributing its final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
// use amongst apps and the sampling frequency does not matter. if (adapter != null) {
bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked(); bluetoothReceiver = new SynchronousResultReceiver();
adapter.requestControllerActivityEnergyInfo(
BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED,
bluetoothReceiver);
}
}
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
if (mTelephony == null) {
mTelephony = TelephonyManager.from(mContext);
}
if (mTelephony != null) {
modemReceiver = new SynchronousResultReceiver();
mTelephony.requestModemActivityInfo(modemReceiver);
}
}
WifiActivityEnergyInfo wifiInfo = null;
BluetoothActivityEnergyInfo bluetoothInfo = null;
ModemActivityInfo modemInfo = null;
try {
wifiInfo = awaitControllerInfo(wifiReceiver);
} catch (TimeoutException e) {
Slog.w(TAG, "Timeout reading wifi stats");
}
try {
bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
} catch (TimeoutException e) {
Slog.w(TAG, "Timeout reading bt stats");
}
try {
modemInfo = awaitControllerInfo(modemReceiver);
} catch (TimeoutException e) {
Slog.w(TAG, "Timeout reading modem stats");
} }
synchronized (mStats) { synchronized (mStats) {
final long elapsedRealtime = SystemClock.elapsedRealtime(); mStats.addHistoryEventLocked(
final long uptime = SystemClock.uptimeMillis(); SystemClock.elapsedRealtime(),
if (mStats.mRecordAllHistory) { SystemClock.uptimeMillis(),
mStats.addHistoryEventLocked(elapsedRealtime, uptime, BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, reason, 0); reason, 0);
mStats.updateCpuTimeLocked();
mStats.updateKernelWakelocksLocked();
if (wifiInfo != null) {
if (wifiInfo.isValid()) {
mStats.updateWifiStateLocked(extractDelta(wifiInfo));
} else {
Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
}
} }
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU) != 0) { if (bluetoothInfo != null) {
mStats.updateCpuTimeLocked(); if (bluetoothInfo.isValid()) {
mStats.updateKernelWakelocksLocked(); mStats.updateBluetoothStateLocked(bluetoothInfo);
} else {
Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
}
} }
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { if (modemInfo != null) {
mStats.updateMobileRadioStateLocked(elapsedRealtime, modemActivityInfo); if (modemInfo.isValid()) {
} mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(),
modemInfo);
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { } else {
mStats.updateWifiStateLocked(wifiEnergyInfo); Slog.e(TAG, "modem info is invalid: " + modemInfo);
} }
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
mStats.updateBluetoothStateLocked(bluetoothEnergyInfo);
} }
} }
} }
@@ -1439,7 +1499,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} }
long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity();
try { try {
updateExternalStats("get-health-stats-for-uid", updateExternalStatsSync("get-health-stats-for-uid",
BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
return getHealthStatsForUidLocked(requestUid); return getHealthStatsForUidLocked(requestUid);
@@ -1464,7 +1524,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity();
int i=-1; int i=-1;
try { try {
updateExternalStats("get-health-stats-for-uids", updateExternalStatsSync("get-health-stats-for-uids",
BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL); BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) { synchronized (mStats) {
final int N = requestUids.length; final int N = requestUids.length;
@@ -1499,7 +1559,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
/** /**
* Gets a HealthStatsParceler for the given uid. You should probably call * Gets a HealthStatsParceler for the given uid. You should probably call
* updateExternalStats first. * updateExternalStatsSync first.
*/ */
HealthStatsParceler getHealthStatsForUidLocked(int requestUid) { HealthStatsParceler getHealthStatsForUidLocked(int requestUid) {
final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter(); final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter();

View File

@@ -25,6 +25,8 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.BatteryStats;
import android.os.ResultReceiver;
import android.provider.Settings; import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException; import android.provider.Settings.SettingNotFoundException;
import android.os.Bundle; import android.os.Bundle;
@@ -73,6 +75,14 @@ import java.util.regex.Pattern;
public class TelephonyManager { public class TelephonyManager {
private static final String TAG = "TelephonyManager"; private static final String TAG = "TelephonyManager";
/**
* The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
* into the ResultReceiver Bundle.
* @hide
*/
public static final String MODEM_ACTIVITY_RESULT_KEY =
BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
private static ITelephonyRegistry sRegistry; private static ITelephonyRegistry sRegistry;
/** /**
@@ -5051,19 +5061,23 @@ public class TelephonyManager {
} }
/** /**
* Returns the modem activity info. * Requests the modem activity info. The recipient will place the result
* in `result`.
* @param result The object on which the recipient will send the resulting
* {@link android.telephony.ModemActivityInfo} object.
* @hide * @hide
*/ */
public ModemActivityInfo getModemActivityInfo() { public void requestModemActivityInfo(ResultReceiver result) {
try { try {
ITelephony service = getITelephony(); ITelephony service = getITelephony();
if (service != null) { if (service != null) {
return service.getModemActivityInfo(); service.requestModemActivityInfo(result);
return;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e); Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
} }
return null; result.send(0, null);
} }
/** /**

View File

@@ -18,6 +18,7 @@ package com.android.internal.telephony;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.ResultReceiver;
import android.net.Uri; import android.net.Uri;
import android.telecom.PhoneAccount; import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle; import android.telecom.PhoneAccountHandle;
@@ -1027,9 +1028,13 @@ interface ITelephony {
String getLocaleFromDefaultSim(); String getLocaleFromDefaultSim();
/** /**
* Return the modem activity info. * Requests the modem activity info asynchronously.
* The implementor is expected to reply with the
* {@link android.telephony.ModemActivityInfo} object placed into the Bundle with the key
* {@link android.telephony.TelephonyManager#MODEM_ACTIVITY_RESULT_KEY}.
* The result code is ignored.
*/ */
ModemActivityInfo getModemActivityInfo(); oneway void requestModemActivityInfo(in ResultReceiver result);
/** /**
* Get the service state on specified subscription * Get the service state on specified subscription

View File

@@ -27,8 +27,8 @@ import android.net.Network;
import android.net.DhcpInfo; import android.net.DhcpInfo;
import android.os.Messenger; import android.os.Messenger;
import android.os.ResultReceiver;
import android.os.WorkSource; import android.os.WorkSource;
/** /**
@@ -42,6 +42,14 @@ interface IWifiManager
WifiActivityEnergyInfo reportActivityInfo(); WifiActivityEnergyInfo reportActivityInfo();
/**
* Requests the controller activity info asynchronously.
* The implementor is expected to reply with the
* {@link android.net.wifi.WifiActivityEnergyInfo} object placed into the Bundle with the key
* {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. The result code is ignored.
*/
oneway void requestActivityInfo(in ResultReceiver result);
List<WifiConfiguration> getConfiguredNetworks(); List<WifiConfiguration> getConfiguredNetworks();
List<WifiConfiguration> getPrivilegedConfiguredNetworks(); List<WifiConfiguration> getPrivilegedConfiguredNetworks();

View File

@@ -181,8 +181,8 @@ public final class WifiActivityEnergyInfo implements Parcelable {
* @return if the record is valid * @return if the record is valid
*/ */
public boolean isValid() { public boolean isValid() {
return ((mControllerTxTimeMs !=0) || return ((mControllerTxTimeMs >=0) &&
(mControllerRxTimeMs !=0) || (mControllerRxTimeMs >=0) &&
(mControllerIdleTimeMs !=0)); (mControllerIdleTimeMs >=0));
} }
} }