diff options
7 files changed, 797 insertions, 544 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 1a06e0a..8b3ecae 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -3233,9 +3233,9 @@ public abstract class BatteryStats implements Parcelable { if (!didOne) sb.append(" (no activity)"); pw.println(sb.toString()); - final long wifiIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which); - final long wifiRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which); - final long wifiTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, which); + final long wifiIdleTimeMs = getWifiControllerActivity(CONTROLLER_IDLE_TIME, which); + final long wifiRxTimeMs = getWifiControllerActivity(CONTROLLER_RX_TIME, which); + final long wifiTxTimeMs = getWifiControllerActivity(CONTROLLER_TX_TIME, which); final long wifiTotalTimeMs = wifiIdleTimeMs + wifiRxTimeMs + wifiTxTimeMs; sb.setLength(0); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 2c34ded..93dc995 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -16,18 +16,14 @@ package com.android.internal.os; -import static android.net.NetworkStats.UID_ALL; -import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; - +import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothActivityEnergyInfo; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkStats; -import android.net.wifi.IWifiManager; import android.net.wifi.WifiActivityEnergyInfo; import android.net.wifi.WifiManager; import android.os.BadParcelableException; @@ -42,8 +38,6 @@ import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.WorkSource; @@ -65,13 +59,14 @@ import android.util.TimeUtils; import android.util.Xml; import android.view.Display; -import com.android.internal.annotations.GuardedBy; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; +import com.android.server.NetworkManagementSocketTagger; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -132,6 +127,9 @@ public final class BatteryStatsImpl extends BatteryStats { static final int MSG_REPORT_POWER_CHANGE = 2; static final long DELAY_UPDATE_WAKELOCKS = 5*1000; + private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); + private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); + public interface BatteryCallback { public void batteryNeedsCpuUpdate(); public void batteryPowerChanged(boolean onBattery); @@ -160,7 +158,12 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public interface ExternalStatsSync { + void scheduleSync(); + } + public final MyHandler mHandler; + private final ExternalStatsSync mExternalSync; private BatteryCallback mCallback; @@ -330,7 +333,7 @@ public final class BatteryStatsImpl extends BatteryStats { int mPhoneSignalStrengthBin = -1; int mPhoneSignalStrengthBinRaw = -1; - final StopwatchTimer[] mPhoneSignalStrengthsTimer = + final StopwatchTimer[] mPhoneSignalStrengthsTimer = new StopwatchTimer[SignalStrength.NUM_SIGNAL_STRENGTH_BINS]; StopwatchTimer mPhoneSignalScanningTimer; @@ -445,18 +448,17 @@ public final class BatteryStatsImpl extends BatteryStats { private int mLoadedNumConnectivityChange; private int mUnpluggedNumConnectivityChange; + private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry(); + /* * Holds a SamplingTimer associated with each kernel wakelock name being tracked. */ - private final HashMap<String, SamplingTimer> mKernelWakelockStats = - new HashMap<String, SamplingTimer>(); + private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>(); public Map<String, ? extends Timer> getKernelWakelockStats() { return mKernelWakelockStats; } - private static int sKernelWakelockUpdateVersion = 0; - String mLastWakeupReason = null; long mLastWakeupUptimeMs = 0; private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>(); @@ -465,55 +467,12 @@ public final class BatteryStatsImpl extends BatteryStats { return mWakeupReasonStats; } - private static final int[] PROC_WAKELOCKS_FORMAT = new int[] { - Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name - Process.PROC_QUOTES, - Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count - Process.PROC_TAB_TERM, - Process.PROC_TAB_TERM, - Process.PROC_TAB_TERM, - Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime - }; - - private static final int[] WAKEUP_SOURCES_FORMAT = new int[] { - Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name - Process.PROC_TAB_TERM|Process.PROC_COMBINE| - Process.PROC_OUT_LONG, // 1: count - Process.PROC_TAB_TERM|Process.PROC_COMBINE, - Process.PROC_TAB_TERM|Process.PROC_COMBINE, - Process.PROC_TAB_TERM|Process.PROC_COMBINE, - Process.PROC_TAB_TERM|Process.PROC_COMBINE, - Process.PROC_TAB_TERM|Process.PROC_COMBINE - |Process.PROC_OUT_LONG, // 6: totalTime - }; - - private final String[] mProcWakelocksName = new String[3]; - private final long[] mProcWakelocksData = new long[3]; - - /* - * Used as a buffer for reading in data from /proc/wakelocks before it is processed and added - * to mKernelWakelockStats. - */ - private final Map<String, KernelWakelockStats> mProcWakelockFileStats = new HashMap<>(); - - private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory(); - private NetworkStats mCurMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50); - private NetworkStats mLastMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50); - private NetworkStats mCurWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50); - private NetworkStats mLastWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50); - private NetworkStats mTmpNetworkStats; - private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry(); - - @GuardedBy("this") - private String[] mMobileIfaces = new String[0]; - @GuardedBy("this") - private String[] mWifiIfaces = new String[0]; - public BatteryStatsImpl() { mFile = null; mCheckinFile = null; mDailyFile = null; mHandler = null; + mExternalSync = null; clearHistoryLocked(); } @@ -523,7 +482,7 @@ public final class BatteryStatsImpl extends BatteryStats { } static class TimeBase { - private final ArrayList<TimeBaseObs> mObservers = new ArrayList<TimeBaseObs>(); + private final ArrayList<TimeBaseObs> mObservers = new ArrayList<>(); private long mUptime; private long mRealtime; @@ -1778,147 +1737,6 @@ public final class BatteryStatsImpl extends BatteryStats { return timer; } - private final Map<String, KernelWakelockStats> readKernelWakelockStats() { - - FileInputStream is; - byte[] buffer = new byte[32*1024]; - int len; - boolean wakeup_sources; - - try { - try { - is = new FileInputStream("/d/wakeup_sources"); - wakeup_sources = true; - } catch (java.io.FileNotFoundException e) { - try { - is = new FileInputStream("/proc/wakelocks"); - wakeup_sources = false; - } catch (java.io.FileNotFoundException e2) { - return null; - } - } - - len = is.read(buffer); - is.close(); - } catch (java.io.IOException e) { - return null; - } - - if (len > 0) { - if (len >= buffer.length) { - Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); - } - int i; - for (i=0; i<len; i++) { - if (buffer[i] == '\0') { - len = i; - break; - } - } - } - - return parseProcWakelocks(buffer, len, wakeup_sources); - } - - private final Map<String, KernelWakelockStats> parseProcWakelocks( - byte[] wlBuffer, int len, boolean wakeup_sources) { - String name; - int count; - long totalTime; - int startIndex; - int endIndex; - int numUpdatedWlNames = 0; - - // Advance past the first line. - int i; - for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++); - startIndex = endIndex = i + 1; - - synchronized(this) { - Map<String, KernelWakelockStats> m = mProcWakelockFileStats; - - sKernelWakelockUpdateVersion++; - while (endIndex < len) { - for (endIndex=startIndex; - endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; - endIndex++); - endIndex++; // endIndex is an exclusive upper bound. - // Don't go over the end of the buffer, Process.parseProcLine might - // write to wlBuffer[endIndex] - if (endIndex >= (len - 1) ) { - return m; - } - - String[] nameStringArray = mProcWakelocksName; - long[] wlData = mProcWakelocksData; - // Stomp out any bad characters since this is from a circular buffer - // A corruption is seen sometimes that results in the vm crashing - // This should prevent crashes and the line will probably fail to parse - for (int j = startIndex; j < endIndex; j++) { - if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; - } - boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, - wakeup_sources ? WAKEUP_SOURCES_FORMAT : - PROC_WAKELOCKS_FORMAT, - nameStringArray, wlData, null); - - name = nameStringArray[0]; - count = (int) wlData[1]; - - if (wakeup_sources) { - // convert milliseconds to microseconds - totalTime = wlData[2] * 1000; - } else { - // convert nanoseconds to microseconds with rounding. - totalTime = (wlData[2] + 500) / 1000; - } - - if (parsed && name.length() > 0) { - if (!m.containsKey(name)) { - m.put(name, new KernelWakelockStats(count, totalTime, - sKernelWakelockUpdateVersion)); - numUpdatedWlNames++; - } else { - KernelWakelockStats kwlStats = m.get(name); - if (kwlStats.mVersion == sKernelWakelockUpdateVersion) { - kwlStats.mCount += count; - kwlStats.mTotalTime += totalTime; - } else { - kwlStats.mCount = count; - kwlStats.mTotalTime = totalTime; - kwlStats.mVersion = sKernelWakelockUpdateVersion; - numUpdatedWlNames++; - } - } - } - startIndex = endIndex; - } - - if (m.size() != numUpdatedWlNames) { - // Don't report old data. - Iterator<KernelWakelockStats> itr = m.values().iterator(); - while (itr.hasNext()) { - if (itr.next().mVersion != sKernelWakelockUpdateVersion) { - itr.remove(); - } - } - } - return m; - } - } - - private class KernelWakelockStats { - public int mCount; - public long mTotalTime; - public int mVersion; - - KernelWakelockStats(int count, long totalTime, int version) { - mCount = count; - mTotalTime = totalTime; - mVersion = version; - } - } - /* * Get the KernelWakelockTimer associated with name, and create a new one if one * doesn't already exist. @@ -3390,7 +3208,7 @@ public final class BatteryStatsImpl extends BatteryStats { mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime); } else { mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs); - updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs); + updateMobileRadioStateLocked(realElapsedRealtimeMs); mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs); } } @@ -3728,6 +3546,7 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); mWifiOn = true; mWifiOnTimer.startRunningLocked(elapsedRealtime); + scheduleSyncExternalStatsLocked(); } } @@ -3741,6 +3560,7 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); mWifiOn = false; mWifiOnTimer.stopRunningLocked(elapsedRealtime); + scheduleSyncExternalStatsLocked(); } } @@ -3903,6 +3723,7 @@ public final class BatteryStatsImpl extends BatteryStats { int uid = mapUid(ws.get(i)); getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime); } + scheduleSyncExternalStatsLocked(); } else { Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running"); } @@ -3941,6 +3762,7 @@ public final class BatteryStatsImpl extends BatteryStats { int uid = mapUid(ws.get(i)); getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime); } + scheduleSyncExternalStatsLocked(); } else { Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running"); } @@ -3955,6 +3777,7 @@ public final class BatteryStatsImpl extends BatteryStats { } mWifiState = wifiState; mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime); + scheduleSyncExternalStatsLocked(); } } @@ -4026,6 +3849,7 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); mBluetoothOn = true; mBluetoothOnTimer.startRunningLocked(elapsedRealtime); + scheduleSyncExternalStatsLocked(); } } @@ -4039,6 +3863,7 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtime, uptime); mBluetoothOn = false; mBluetoothOnTimer.stopRunningLocked(elapsedRealtime); + scheduleSyncExternalStatsLocked(); } } @@ -4065,6 +3890,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + scheduleSyncExternalStatsLocked(); } mWifiFullLockNesting++; getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime); @@ -4080,6 +3906,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + scheduleSyncExternalStatsLocked(); } getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime); } @@ -4137,6 +3964,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + scheduleSyncExternalStatsLocked(); } mWifiMulticastNesting++; getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime); @@ -4152,6 +3980,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + scheduleSyncExternalStatsLocked(); } getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime); } @@ -4259,7 +4088,8 @@ public final class BatteryStatsImpl extends BatteryStats { // During device boot, qtaguid isn't enabled until after the inital // loading of battery stats. Now that they're enabled, take our initial // snapshot for future delta calculation. - updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime()); + updateMobileRadioStateLocked(SystemClock.elapsedRealtime()); + updateWifiStateLocked(null); } @Override public long getScreenOnTime(long elapsedRealtimeUs, int which) { @@ -5949,7 +5779,7 @@ public final class BatteryStatsImpl extends BatteryStats { Slog.w(TAG, "File corrupt: too many excessive power entries " + N); return false; } - + mExcessivePower = new ArrayList<ExcessivePower>(); for (int i=0; i<N; i++) { ExcessivePower ew = new ExcessivePower(); @@ -6727,7 +6557,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public BatteryStatsImpl(File systemDir, Handler handler) { + public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) { if (systemDir != null) { mFile = new JournaledFile(new File(systemDir, "batterystats.bin"), new File(systemDir, "batterystats.bin.tmp")); @@ -6736,6 +6566,7 @@ public final class BatteryStatsImpl extends BatteryStats { } mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin")); mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml")); + mExternalSync = externalSync; mHandler = new MyHandler(handler.getLooper()); mStartCount++; mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase); @@ -6808,6 +6639,7 @@ public final class BatteryStatsImpl extends BatteryStats { mCheckinFile = null; mDailyFile = null; mHandler = null; + mExternalSync = null; clearHistoryLocked(); readFromParcel(p); } @@ -7479,21 +7311,233 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel; } } - + public void pullPendingStateUpdatesLocked() { - updateKernelWakelocksLocked(); - updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime()); - // TODO(adamlesinski): enable when bluedroid stops deadlocking. b/19248786 - // updateBluetoothControllerActivityLocked(); - // TODO(adamlesinski): disabled to avoid deadlock. Need to change how external - // data is pulled/accessed from BatteryStats. b/19729960 - // updateWifiControllerActivityLocked(); if (mOnBatteryInternal) { final boolean screenOn = mScreenState == Display.STATE_ON; updateDischargeScreenLevelsLocked(screenOn, screenOn); } } + private String[] mMobileIfaces = EmptyArray.STRING; + private String[] mWifiIfaces = EmptyArray.STRING; + + private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory(); + + private static final int NETWORK_STATS_LAST = 0; + private static final int NETWORK_STATS_NEXT = 1; + private static final int NETWORK_STATS_DELTA = 2; + + private final NetworkStats[] mMobileNetworkStats = new NetworkStats[] { + new NetworkStats(SystemClock.elapsedRealtime(), 50), + new NetworkStats(SystemClock.elapsedRealtime(), 50), + new NetworkStats(SystemClock.elapsedRealtime(), 50) + }; + + private final NetworkStats[] mWifiNetworkStats = new NetworkStats[] { + new NetworkStats(SystemClock.elapsedRealtime(), 50), + new NetworkStats(SystemClock.elapsedRealtime(), 50), + new NetworkStats(SystemClock.elapsedRealtime(), 50) + }; + + /** + * Retrieves the delta of network stats for the given network ifaces. Uses networkStatsBuffer + * as a buffer of NetworkStats objects to cycle through when computing deltas. + */ + private NetworkStats getNetworkStatsDeltaLocked(String[] ifaces, + NetworkStats[] networkStatsBuffer) + throws IOException { + if (!SystemProperties.getBoolean(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED, + false)) { + return null; + } + + final NetworkStats stats = mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL, + ifaces, NetworkStats.TAG_NONE, networkStatsBuffer[NETWORK_STATS_NEXT]); + networkStatsBuffer[NETWORK_STATS_DELTA] = NetworkStats.subtract(stats, + networkStatsBuffer[NETWORK_STATS_LAST], null, null, + networkStatsBuffer[NETWORK_STATS_DELTA]); + networkStatsBuffer[NETWORK_STATS_NEXT] = networkStatsBuffer[NETWORK_STATS_LAST]; + networkStatsBuffer[NETWORK_STATS_LAST] = stats; + return networkStatsBuffer[NETWORK_STATS_DELTA]; + } + + /** + * Distribute WiFi energy info and network traffic to apps. + * @param info The energy information from the WiFi controller. + */ + public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) { + final NetworkStats delta; + try { + delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats); + } catch (IOException e) { + Slog.wtf(TAG, "Failed to get wifi network stats", e); + return; + } + + if (!mOnBatteryInternal) { + return; + } + + if (delta != null) { + final int size = delta.size(); + for (int i = 0; i < size; i++) { + final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry); + + if (DEBUG) { + Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes + + " tx=" + entry.txBytes); + } + + if (entry.rxBytes == 0 || entry.txBytes == 0) { + continue; + } + + final Uid u = getUidStatsLocked(mapUid(entry.uid)); + u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes, + entry.rxPackets); + u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes, + entry.txPackets); + + mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( + entry.rxBytes); + mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( + entry.txBytes); + mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( + entry.rxPackets); + mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( + entry.txPackets); + } + } + + if (info != null) { + // Update WiFi controller stats. + mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked( + info.getControllerRxTimeMillis()); + mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked( + info.getControllerTxTimeMillis()); + mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked( + info.getControllerIdleTimeMillis()); + mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked( + info.getControllerEnergyUsed()); + } + } + + /** + * Distribute Cell radio energy info and network traffic to apps. + */ + public void updateMobileRadioStateLocked(long elapsedRealtimeMs) { + final NetworkStats delta; + + try { + delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats); + } catch (IOException e) { + Slog.wtf(TAG, "Failed to get mobile network stats", e); + return; + } + + if (delta == null || !mOnBatteryInternal) { + return; + } + + long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs); + long totalPackets = delta.getTotalPackets(); + + final int size = delta.size(); + for (int i = 0; i < size; i++) { + final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry); + + if (entry.rxBytes == 0 || entry.txBytes == 0) continue; + + final Uid u = getUidStatsLocked(mapUid(entry.uid)); + u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes, + entry.rxPackets); + u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes, + entry.txPackets); + + if (radioTime > 0) { + // Distribute total radio active time in to this app. + long appPackets = entry.rxPackets + entry.txPackets; + long appRadioTime = (radioTime*appPackets)/totalPackets; + u.noteMobileRadioActiveTimeLocked(appRadioTime); + // Remove this app from the totals, so that we don't lose any time + // due to rounding. + radioTime -= appRadioTime; + totalPackets -= appPackets; + } + + mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked( + entry.rxBytes); + mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked( + entry.txBytes); + mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked( + entry.rxPackets); + mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked( + entry.txPackets); + } + + if (radioTime > 0) { + // Whoops, there is some radio time we can't blame on an app! + mMobileRadioActiveUnknownTime.addCountLocked(radioTime); + mMobileRadioActiveUnknownCount.addCountLocked(1); + } + } + + /** + * Distribute Bluetooth energy info and network traffic to apps. + * @param info The energy information from the bluetooth controller. + */ + public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) { + if (info != null && mOnBatteryInternal) { + mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked( + info.getControllerRxTimeMillis()); + mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked( + info.getControllerTxTimeMillis()); + mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked( + info.getControllerIdleTimeMillis()); + mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked( + info.getControllerEnergyUsed()); + } + } + + /** + * Read and distribute kernel wake lock use across apps. + */ + public void updateKernelWakelocksLocked() { + final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats( + mTmpWakelockStats); + if (wakelockStats == null) { + // Not crashing might make board bringup easier. + Slog.w(TAG, "Couldn't get kernel wake lock stats"); + return; + } + + for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) { + String name = ent.getKey(); + KernelWakelockStats.Entry kws = ent.getValue(); + + SamplingTimer kwlt = mKernelWakelockStats.get(name); + if (kwlt == null) { + kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase, + true /* track reported val */); + mKernelWakelockStats.put(name, kwlt); + } + kwlt.updateCurrentReportedCount(kws.mCount); + kwlt.updateCurrentReportedTotalTime(kws.mTotalTime); + kwlt.setUpdateVersion(kws.mVersion); + } + + if (wakelockStats.size() != mKernelWakelockStats.size()) { + // Set timers to stale if they didn't appear in /proc/wakelocks this time. + for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) { + SamplingTimer st = ent.getValue(); + if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) { + st.setStale(); + } + } + } + } + void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level) { boolean doWrite = false; @@ -7647,340 +7691,132 @@ public final class BatteryStatsImpl extends BatteryStats { } } + private void scheduleSyncExternalStatsLocked() { + if (mExternalSync != null) { + mExternalSync.scheduleSync(); + } + } + // This should probably be exposed in the API, though it's not critical - private static final int BATTERY_PLUGGED_NONE = 0; + public static final int BATTERY_PLUGGED_NONE = 0; - public void setBatteryState(int status, int health, int plugType, int level, + public void setBatteryStateLocked(int status, int health, int plugType, int level, int temp, int volt) { - synchronized(this) { - final boolean onBattery = plugType == BATTERY_PLUGGED_NONE; - final long uptime = SystemClock.uptimeMillis(); - final long elapsedRealtime = SystemClock.elapsedRealtime(); - if (!mHaveBatteryLevel) { - mHaveBatteryLevel = true; - // We start out assuming that the device is plugged in (not - // on battery). If our first report is now that we are indeed - // plugged in, then twiddle our state to correctly reflect that - // since we won't be going through the full setOnBattery(). - if (onBattery == mOnBattery) { - if (onBattery) { - mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; - } else { - mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; - } - } - mHistoryCur.batteryStatus = (byte)status; - mHistoryCur.batteryLevel = (byte)level; - mMaxChargeStepLevel = mMinDischargeStepLevel = - mLastChargeStepLevel = mLastDischargeStepLevel = level; - } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) { - recordDailyStatsIfNeededLocked(level >= 100 && onBattery); - } - int oldStatus = mHistoryCur.batteryStatus; - if (onBattery) { - mDischargeCurrentLevel = level; - if (!mRecordingHistory) { - mRecordingHistory = true; - startRecordingHistory(elapsedRealtime, uptime, true); - } - } else if (level < 96) { - if (!mRecordingHistory) { - mRecordingHistory = true; - startRecordingHistory(elapsedRealtime, uptime, true); - } - } - mCurrentBatteryLevel = level; - if (mDischargePlugLevel < 0) { - mDischargePlugLevel = level; - } - if (onBattery != mOnBattery) { - mHistoryCur.batteryLevel = (byte)level; - mHistoryCur.batteryStatus = (byte)status; - mHistoryCur.batteryHealth = (byte)health; - mHistoryCur.batteryPlugType = (byte)plugType; - mHistoryCur.batteryTemperature = (short)temp; - mHistoryCur.batteryVoltage = (char)volt; - setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level); - } else { - boolean changed = false; - if (mHistoryCur.batteryLevel != level) { - mHistoryCur.batteryLevel = (byte)level; - changed = true; - } - if (mHistoryCur.batteryStatus != status) { - mHistoryCur.batteryStatus = (byte)status; - changed = true; - } - if (mHistoryCur.batteryHealth != health) { - mHistoryCur.batteryHealth = (byte)health; - changed = true; - } - if (mHistoryCur.batteryPlugType != plugType) { - mHistoryCur.batteryPlugType = (byte)plugType; - changed = true; - } - if (temp >= (mHistoryCur.batteryTemperature+10) - || temp <= (mHistoryCur.batteryTemperature-10)) { - mHistoryCur.batteryTemperature = (short)temp; - changed = true; - } - if (volt > (mHistoryCur.batteryVoltage+20) - || volt < (mHistoryCur.batteryVoltage-20)) { - mHistoryCur.batteryVoltage = (char)volt; - changed = true; - } - if (changed) { - addHistoryRecordLocked(elapsedRealtime, uptime); - } - long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT) - | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT) - | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT); + final boolean onBattery = plugType == BATTERY_PLUGGED_NONE; + final long uptime = SystemClock.uptimeMillis(); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + if (!mHaveBatteryLevel) { + mHaveBatteryLevel = true; + // We start out assuming that the device is plugged in (not + // on battery). If our first report is now that we are indeed + // plugged in, then twiddle our state to correctly reflect that + // since we won't be going through the full setOnBattery(). + if (onBattery == mOnBattery) { if (onBattery) { - if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) { - mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level, - modeBits, elapsedRealtime); - mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level, - modeBits, elapsedRealtime); - mLastDischargeStepLevel = level; - mMinDischargeStepLevel = level; - mInitStepMode = mCurStepMode; - mModStepMode = 0; - } + mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; } else { - if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) { - mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel, - modeBits, elapsedRealtime); - mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel, - modeBits, elapsedRealtime); - mLastChargeStepLevel = level; - mMaxChargeStepLevel = level; - mInitStepMode = mCurStepMode; - mModStepMode = 0; - } + mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; } } - if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) { - // We don't record history while we are plugged in and fully charged. - // The next time we are unplugged, history will be cleared. - mRecordingHistory = DEBUG; + mHistoryCur.batteryStatus = (byte)status; + mHistoryCur.batteryLevel = (byte)level; + mMaxChargeStepLevel = mMinDischargeStepLevel = + mLastChargeStepLevel = mLastDischargeStepLevel = level; + } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) { + recordDailyStatsIfNeededLocked(level >= 100 && onBattery); + } + int oldStatus = mHistoryCur.batteryStatus; + if (onBattery) { + mDischargeCurrentLevel = level; + if (!mRecordingHistory) { + mRecordingHistory = true; + startRecordingHistory(elapsedRealtime, uptime, true); + } + } else if (level < 96) { + if (!mRecordingHistory) { + mRecordingHistory = true; + startRecordingHistory(elapsedRealtime, uptime, true); } } - } - - public void updateKernelWakelocksLocked() { - Map<String, KernelWakelockStats> m = readKernelWakelockStats(); - - if (m == null) { - // Not crashing might make board bringup easier. - Slog.w(TAG, "Couldn't get kernel wake lock stats"); - return; + mCurrentBatteryLevel = level; + if (mDischargePlugLevel < 0) { + mDischargePlugLevel = level; } + if (onBattery != mOnBattery) { + mHistoryCur.batteryLevel = (byte)level; + mHistoryCur.batteryStatus = (byte)status; + mHistoryCur.batteryHealth = (byte)health; + mHistoryCur.batteryPlugType = (byte)plugType; + mHistoryCur.batteryTemperature = (short)temp; + mHistoryCur.batteryVoltage = (char)volt; + setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level); + } else { + boolean changed = false; + if (mHistoryCur.batteryLevel != level) { + mHistoryCur.batteryLevel = (byte)level; + changed = true; - for (Map.Entry<String, KernelWakelockStats> ent : m.entrySet()) { - String name = ent.getKey(); - KernelWakelockStats kws = ent.getValue(); - - SamplingTimer kwlt = mKernelWakelockStats.get(name); - if (kwlt == null) { - kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase, - true /* track reported val */); - mKernelWakelockStats.put(name, kwlt); + // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record + // which will pull external stats. + scheduleSyncExternalStatsLocked(); } - kwlt.updateCurrentReportedCount(kws.mCount); - kwlt.updateCurrentReportedTotalTime(kws.mTotalTime); - kwlt.setUpdateVersion(sKernelWakelockUpdateVersion); - } - - if (m.size() != mKernelWakelockStats.size()) { - // Set timers to stale if they didn't appear in /proc/wakelocks this time. - for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) { - SamplingTimer st = ent.getValue(); - if (st.getUpdateVersion() != sKernelWakelockUpdateVersion) { - st.setStale(); - } + if (mHistoryCur.batteryStatus != status) { + mHistoryCur.batteryStatus = (byte)status; + changed = true; } - } - } - - static final int NET_UPDATE_MOBILE = 1<<0; - static final int NET_UPDATE_WIFI = 1<<1; - static final int NET_UPDATE_ALL = 0xffff; - - private void updateNetworkActivityLocked(int which, long elapsedRealtimeMs) { - if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return; - - if ((which&NET_UPDATE_MOBILE) != 0 && mMobileIfaces.length > 0) { - final NetworkStats snapshot; - final NetworkStats last = mCurMobileSnapshot; - try { - snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL, - mMobileIfaces, NetworkStats.TAG_NONE, mLastMobileSnapshot); - } catch (IOException e) { - Log.wtf(TAG, "Failed to read mobile network stats", e); - return; + if (mHistoryCur.batteryHealth != health) { + mHistoryCur.batteryHealth = (byte)health; + changed = true; } - - mCurMobileSnapshot = snapshot; - mLastMobileSnapshot = last; - - if (mOnBatteryInternal) { - final NetworkStats delta = NetworkStats.subtract(snapshot, last, - null, null, mTmpNetworkStats); - mTmpNetworkStats = delta; - - long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked( - elapsedRealtimeMs); - long totalPackets = delta.getTotalPackets(); - - final int size = delta.size(); - for (int i = 0; i < size; i++) { - final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry); - - if (entry.rxBytes == 0 || entry.txBytes == 0) continue; - - final Uid u = getUidStatsLocked(mapUid(entry.uid)); - u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes, - entry.rxPackets); - u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes, - entry.txPackets); - - if (radioTime > 0) { - // Distribute total radio active time in to this app. - long appPackets = entry.rxPackets + entry.txPackets; - long appRadioTime = (radioTime*appPackets)/totalPackets; - u.noteMobileRadioActiveTimeLocked(appRadioTime); - // Remove this app from the totals, so that we don't lose any time - // due to rounding. - radioTime -= appRadioTime; - totalPackets -= appPackets; - } - - mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked( - entry.rxBytes); - mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked( - entry.txBytes); - mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked( - entry.rxPackets); - mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked( - entry.txPackets); - } - - if (radioTime > 0) { - // Whoops, there is some radio time we can't blame on an app! - mMobileRadioActiveUnknownTime.addCountLocked(radioTime); - mMobileRadioActiveUnknownCount.addCountLocked(1); - } + if (mHistoryCur.batteryPlugType != plugType) { + mHistoryCur.batteryPlugType = (byte)plugType; + changed = true; } - } - - if ((which&NET_UPDATE_WIFI) != 0 && mWifiIfaces.length > 0) { - final NetworkStats snapshot; - final NetworkStats last = mCurWifiSnapshot; - try { - snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL, - mWifiIfaces, NetworkStats.TAG_NONE, mLastWifiSnapshot); - } catch (IOException e) { - Log.wtf(TAG, "Failed to read wifi network stats", e); - return; + if (temp >= (mHistoryCur.batteryTemperature+10) + || temp <= (mHistoryCur.batteryTemperature-10)) { + mHistoryCur.batteryTemperature = (short)temp; + changed = true; } - - mCurWifiSnapshot = snapshot; - mLastWifiSnapshot = last; - - if (mOnBatteryInternal) { - final NetworkStats delta = NetworkStats.subtract(snapshot, last, - null, null, mTmpNetworkStats); - mTmpNetworkStats = delta; - - final int size = delta.size(); - for (int i = 0; i < size; i++) { - final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry); - - if (DEBUG) { - final NetworkStats.Entry cur = snapshot.getValues(i, null); - Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes - + " tx=" + entry.txBytes + ", cur rx=" + cur.rxBytes - + " tx=" + cur.txBytes); - } - - if (entry.rxBytes == 0 || entry.txBytes == 0) continue; - - final Uid u = getUidStatsLocked(mapUid(entry.uid)); - u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes, - entry.rxPackets); - u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes, - entry.txPackets); - - mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( - entry.rxBytes); - mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( - entry.txBytes); - mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( - entry.rxPackets); - mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( - entry.txPackets); + if (volt > (mHistoryCur.batteryVoltage+20) + || volt < (mHistoryCur.batteryVoltage-20)) { + mHistoryCur.batteryVoltage = (char)volt; + changed = true; + } + if (changed) { + addHistoryRecordLocked(elapsedRealtime, uptime); + } + long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT) + | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT) + | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT); + if (onBattery) { + if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) { + mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level, + modeBits, elapsedRealtime); + mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level, + modeBits, elapsedRealtime); + mLastDischargeStepLevel = level; + mMinDischargeStepLevel = level; + mInitStepMode = mCurStepMode; + mModStepMode = 0; + } + } else { + if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) { + mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel, + modeBits, elapsedRealtime); + mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel, + modeBits, elapsedRealtime); + mLastChargeStepLevel = level; + mMaxChargeStepLevel = level; + mInitStepMode = mCurStepMode; + mModStepMode = 0; } } } - } - - private void updateBluetoothControllerActivityLocked() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter == null) { - return; - } - - // We read the data even if we are not on battery. Each read clears - // the previous data, so we must always read to make sure the - // data is for the current interval. - BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo( - BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED); - if (info == null || !info.isValid() || !mOnBatteryInternal) { - // Bad info or we are not on battery. - return; - } - - mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked( - info.getControllerRxTimeMillis()); - mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked( - info.getControllerTxTimeMillis()); - mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked( - info.getControllerIdleTimeMillis()); - mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked( - info.getControllerEnergyUsed()); - } - - private void updateWifiControllerActivityLocked() { - IWifiManager wifiManager = IWifiManager.Stub.asInterface( - ServiceManager.getService(Context.WIFI_SERVICE)); - if (wifiManager == null) { - return; - } - - WifiActivityEnergyInfo info; - try { - // We read the data even if we are not on battery. Each read clears - // the previous data, so we must always read to make sure the - // data is for the current interval. - info = wifiManager.reportActivityInfo(); - } catch (RemoteException e) { - // Nothing to report, WiFi is dead. - return; + if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) { + // We don't record history while we are plugged in and fully charged. + // The next time we are unplugged, history will be cleared. + mRecordingHistory = DEBUG; } - - if (info == null || !info.isValid() || !mOnBatteryInternal) { - // Bad info or we are not on battery. - return; - } - - mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked( - info.getControllerRxTimeMillis()); - mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked( - info.getControllerTxTimeMillis()); - mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked( - info.getControllerIdleTimeMillis()); - mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked( - info.getControllerEnergyUsed()); } public long getAwakeTimeBattery() { diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java new file mode 100644 index 0000000..768d586 --- /dev/null +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2015 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 com.android.internal.os; + +import android.os.Process; +import android.util.Slog; + +import java.io.FileInputStream; +import java.util.Iterator; + +/** + * Reads and parses wakelock stats from the kernel (/proc/wakelocks). + */ +public class KernelWakelockReader { + private static final String TAG = "KernelWakelockReader"; + private static int sKernelWakelockUpdateVersion = 0; + private static final String sWakelockFile = "/proc/wakelocks"; + private static final String sWakeupSourceFile = "/d/wakeup_sources"; + + private static final int[] PROC_WAKELOCKS_FORMAT = new int[] { + Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name + Process.PROC_QUOTES, + Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 1: count + Process.PROC_TAB_TERM, + Process.PROC_TAB_TERM, + Process.PROC_TAB_TERM, + Process.PROC_TAB_TERM|Process.PROC_OUT_LONG, // 5: totalTime + }; + + private static final int[] WAKEUP_SOURCES_FORMAT = new int[] { + Process.PROC_TAB_TERM|Process.PROC_OUT_STRING, // 0: name + Process.PROC_TAB_TERM|Process.PROC_COMBINE| + Process.PROC_OUT_LONG, // 1: count + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE, + Process.PROC_TAB_TERM|Process.PROC_COMBINE + |Process.PROC_OUT_LONG, // 6: totalTime + }; + + private final String[] mProcWakelocksName = new String[3]; + private final long[] mProcWakelocksData = new long[3]; + + /** + * Reads kernel wakelock stats and updates the staleStats with the new information. + * @param staleStats Existing object to update. + * @return the updated data. + */ + public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) { + byte[] buffer = new byte[32*1024]; + int len; + boolean wakeup_sources; + + try { + FileInputStream is; + try { + is = new FileInputStream(sWakeupSourceFile); + wakeup_sources = true; + } catch (java.io.FileNotFoundException e) { + try { + is = new FileInputStream(sWakelockFile); + wakeup_sources = false; + } catch (java.io.FileNotFoundException e2) { + return null; + } + } + + len = is.read(buffer); + is.close(); + } catch (java.io.IOException e) { + return null; + } + + if (len > 0) { + if (len >= buffer.length) { + Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); + } + int i; + for (i=0; i<len; i++) { + if (buffer[i] == '\0') { + len = i; + break; + } + } + } + return parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + } + + /** + * Reads the wakelocks and updates the staleStats with the new information. + */ + private KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources, + final KernelWakelockStats staleStats) { + String name; + int count; + long totalTime; + int startIndex; + int endIndex; + int numUpdatedWlNames = 0; + + // Advance past the first line. + int i; + for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++); + startIndex = endIndex = i + 1; + + synchronized(this) { + sKernelWakelockUpdateVersion++; + while (endIndex < len) { + for (endIndex=startIndex; + endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; + endIndex++); + endIndex++; // endIndex is an exclusive upper bound. + // Don't go over the end of the buffer, Process.parseProcLine might + // write to wlBuffer[endIndex] + if (endIndex >= (len - 1) ) { + return staleStats; + } + + String[] nameStringArray = mProcWakelocksName; + long[] wlData = mProcWakelocksData; + // Stomp out any bad characters since this is from a circular buffer + // A corruption is seen sometimes that results in the vm crashing + // This should prevent crashes and the line will probably fail to parse + for (int j = startIndex; j < endIndex; j++) { + if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?'; + } + boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex, + wakeup_sources ? WAKEUP_SOURCES_FORMAT : + PROC_WAKELOCKS_FORMAT, + nameStringArray, wlData, null); + + name = nameStringArray[0]; + count = (int) wlData[1]; + + if (wakeup_sources) { + // convert milliseconds to microseconds + totalTime = wlData[2] * 1000; + } else { + // convert nanoseconds to microseconds with rounding. + totalTime = (wlData[2] + 500) / 1000; + } + + if (parsed && name.length() > 0) { + if (!staleStats.containsKey(name)) { + staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime, + sKernelWakelockUpdateVersion)); + numUpdatedWlNames++; + } else { + KernelWakelockStats.Entry kwlStats = staleStats.get(name); + if (kwlStats.mVersion == sKernelWakelockUpdateVersion) { + kwlStats.mCount += count; + kwlStats.mTotalTime += totalTime; + } else { + kwlStats.mCount = count; + kwlStats.mTotalTime = totalTime; + kwlStats.mVersion = sKernelWakelockUpdateVersion; + numUpdatedWlNames++; + } + } + } + startIndex = endIndex; + } + + if (staleStats.size() != numUpdatedWlNames) { + // Don't report old data. + Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator(); + while (itr.hasNext()) { + if (itr.next().mVersion != sKernelWakelockUpdateVersion) { + itr.remove(); + } + } + } + + staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion; + return staleStats; + } + } +} diff --git a/core/java/com/android/internal/os/KernelWakelockStats.java b/core/java/com/android/internal/os/KernelWakelockStats.java new file mode 100644 index 0000000..144ea00 --- /dev/null +++ b/core/java/com/android/internal/os/KernelWakelockStats.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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 com.android.internal.os; + +import java.util.HashMap; + +/** + * Kernel wakelock stats object. + */ +public class KernelWakelockStats extends HashMap<String, KernelWakelockStats.Entry> { + public static class Entry { + public int mCount; + public long mTotalTime; + public int mVersion; + + Entry(int count, long totalTime, int version) { + mCount = count; + mTotalTime = totalTime; + mVersion = version; + } + } + + int kernelWakelockVersion; +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3b779b7..b5e1de9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2188,7 +2188,7 @@ public final class ActivityManagerService extends ActivityManagerNative systemDir.mkdirs(); mBatteryStatsService = new BatteryStatsService(systemDir, mHandler); mBatteryStatsService.getActiveStatistics().readLocked(); - mBatteryStatsService.getActiveStatistics().writeAsyncLocked(); + mBatteryStatsService.scheduleWriteToDisk(); mOnBattery = DEBUG_POWER ? true : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); mBatteryStatsService.getActiveStatistics().setCallback(this); @@ -2432,7 +2432,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (mLastWriteTime < (now-BATTERY_STATS_TIME)) { mLastWriteTime = now; - mBatteryStatsService.getActiveStatistics().writeAsyncLocked(); + mBatteryStatsService.scheduleWriteToDisk(); } } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 80101f5..c8db3be 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -16,20 +16,26 @@ package com.android.server.am; +import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.net.wifi.IWifiManager; +import android.net.wifi.WifiActivityEnergyInfo; import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PowerManagerInternal; import android.os.Process; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; @@ -38,10 +44,12 @@ import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.PowerProfile; +import com.android.server.FgThread; import com.android.server.LocalServices; import java.io.File; @@ -59,15 +67,52 @@ public final class BatteryStatsService extends IBatteryStats.Stub static final String TAG = "BatteryStatsService"; static IBatteryStats sService; - final BatteryStatsImpl mStats; + final BatteryStatsHandler mHandler; Context mContext; private boolean mBluetoothPendingStats; private BluetoothHeadset mBluetoothHeadset; PowerManagerInternal mPowerManagerInternal; + class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync { + public static final int MSG_SYNC_EXTERNAL_STATS = 1; + public static final int MSG_WRITE_TO_DISK = 2; + + public BatteryStatsHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SYNC_EXTERNAL_STATS: + updateExternalStats(); + break; + + case MSG_WRITE_TO_DISK: + updateExternalStats(); + synchronized (mStats) { + mStats.writeAsyncLocked(); + } + break; + } + } + + @Override + public void scheduleSync() { + if (!hasMessages(MSG_SYNC_EXTERNAL_STATS)) { + sendEmptyMessage(MSG_SYNC_EXTERNAL_STATS); + } + } + } + BatteryStatsService(File systemDir, Handler handler) { - mStats = new BatteryStatsImpl(systemDir, handler); + // Our handler here will be accessing the disk, use a different thread than + // what the ActivityManagerService gave us (no I/O on that one!). + mHandler = new BatteryStatsHandler(FgThread.getHandler().getLooper()); + + // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through. + mStats = new BatteryStatsImpl(systemDir, handler, mHandler); } public void publish(Context context) { @@ -92,6 +137,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void shutdown() { Slog.w("BatteryStats", "Writing battery stats before shutdown..."); + + updateExternalStats(); synchronized (mStats) { mStats.shutdownLocked(); } @@ -122,6 +169,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub return mStats; } + /** + * Schedules a write to disk to occur. This will cause the BatteryStatsImpl + * object to update with the latest info, then write to disk. + */ + public void scheduleWriteToDisk() { + mHandler.sendEmptyMessage(BatteryStatsHandler.MSG_WRITE_TO_DISK); + } + // These are for direct use by the activity manager... void addIsolatedUid(int isolatedUid, int appUid) { @@ -174,7 +229,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub //Slog.i("foo", "SENDING BATTERY INFO:"); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); Parcel out = Parcel.obtain(); - mStats.writeToParcel(out, 0); + updateExternalStats(); + synchronized (mStats) { + mStats.writeToParcel(out, 0); + } byte[] data = out.marshall(); out.recycle(); return data; @@ -186,7 +244,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub //Slog.i("foo", "SENDING BATTERY INFO:"); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); Parcel out = Parcel.obtain(); - mStats.writeToParcel(out, 0); + updateExternalStats(); + synchronized (mStats) { + mStats.writeToParcel(out, 0); + } byte[] data = out.marshall(); out.recycle(); try { @@ -663,6 +724,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + @Override public void noteWifiMulticastDisabledFromSource(WorkSource ws) { enforceCallingPermission(); synchronized (mStats) { @@ -671,10 +733,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override - public void noteNetworkInterfaceType(String iface, int type) { + public void noteNetworkInterfaceType(String iface, int networkType) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteNetworkInterfaceTypeLocked(iface, type); + mStats.noteNetworkInterfaceTypeLocked(iface, networkType); } } @@ -715,7 +777,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void setBatteryState(int status, int health, int plugType, int level, int temp, int volt) { enforceCallingPermission(); - mStats.setBatteryState(status, health, plugType, level, temp, volt); + synchronized (mStats) { + final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE; + if (mStats.isOnBattery() == onBattery) { + // The battery state has not changed, so we don't need to sync external + // stats immediately. + mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt); + return; + } + } + + // 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. + updateExternalStats(); + synchronized (mStats) { + mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt); + } } public long getAwakeTimeBattery() { @@ -817,6 +894,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub return i; } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -865,7 +943,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub pw.println("Battery stats reset."); noOutput = true; } + updateExternalStats(); } else if ("--write".equals(arg)) { + updateExternalStats(); synchronized (mStats) { mStats.writeSyncLocked(); pw.println("Battery stats written."); @@ -934,6 +1014,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub flags &= ~BatteryStats.DUMP_INCLUDE_HISTORY; } } + + // Fetch data from external sources and update the BatteryStatsImpl object with them. + updateExternalStats(); + if (useCheckinFormat) { List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0); if (isRealCheckin) { @@ -948,7 +1032,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler); + null, mStats.mHandler, null); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpCheckinLocked(mContext, pw, apps, flags, @@ -978,4 +1062,85 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } } + + // Objects for extracting data from external sources. + private final Object mExternalStatsLock = new Object(); + + @GuardedBy("mExternalStatsLock") + private IWifiManager mWifiManager; + + // 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, 0, 0, 0); + + @GuardedBy("mExternalStatsLock") + private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() { + if (mWifiManager == null) { + mWifiManager = IWifiManager.Stub.asInterface( + ServiceManager.getService(Context.WIFI_SERVICE)); + if (mWifiManager == null) { + return null; + } + } + + try { + // We read the data even if we are not on battery. This is so that we keep the + // correct delta from when we should start reading (aka when we are on battery). + WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo(); + if (info != null && info.isValid()) { + // We will modify the last info object to be the delta, and store the new + // WifiActivityEnergyInfo object as our last one. + final WifiActivityEnergyInfo result = mLastInfo; + result.mTimestamp = info.getTimeStamp(); + result.mStackState = info.getStackState(); + result.mControllerTxTimeMs = + info.getControllerTxTimeMillis()- mLastInfo.mControllerTxTimeMs; + result.mControllerRxTimeMs = + info.getControllerRxTimeMillis() - mLastInfo.mControllerRxTimeMs; + result.mControllerIdleTimeMs = + info.getControllerIdleTimeMillis() - mLastInfo.mControllerIdleTimeMs; + result.mControllerEnergyUsed = + info.getControllerEnergyUsed() - mLastInfo.mControllerEnergyUsed; + mLastInfo = info; + return result; + } + } catch (RemoteException e) { + // Nothing to report, WiFi is dead. + } + return null; + } + + @GuardedBy("mExternalStatsLock") + private BluetoothActivityEnergyInfo pullBluetoothEnergyInfoLocked() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo( + BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED); + if (info != null && info.isValid()) { + return info; + } + } + return null; + } + + /** + * Fetches data from external sources (WiFi controller, bluetooth chipset) and updates + * batterystats with that information. + * + * We first grab a lock specific to this method, then once all the data has been collected, + * we grab the mStats lock and update the data. + */ + void updateExternalStats() { + synchronized (mExternalStatsLock) { + final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked(); + final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked(); + synchronized (mStats) { + mStats.updateKernelWakelocksLocked(); + mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime()); + mStats.updateWifiStateLocked(wifiEnergyInfo); + mStats.updateBluetoothStateLocked(bluetoothEnergyInfo); + } + } + } } diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java index 6263463..9284796 100644 --- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java +++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java @@ -26,12 +26,35 @@ import android.os.Parcelable; * @hide */ public final class WifiActivityEnergyInfo implements Parcelable { - private final long mTimestamp; - private final int mStackState; - private final int mControllerTxTimeMs; - private final int mControllerRxTimeMs; - private final int mControllerIdleTimeMs; - private final int mControllerEnergyUsed; + /** + * @hide + */ + public long mTimestamp; + + /** + * @hide + */ + public int mStackState; + + /** + * @hide + */ + public int mControllerTxTimeMs; + + /** + * @hide + */ + public int mControllerRxTimeMs; + + /** + * @hide + */ + public int mControllerIdleTimeMs; + + /** + * @hide + */ + public int mControllerEnergyUsed; public static final int STACK_STATE_INVALID = 0; public static final int STACK_STATE_STATE_ACTIVE = 1; |