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; | 
