Merge "TIF: Redefine time shift status and change callback and position APIs"
diff --git a/api/current.txt b/api/current.txt
index d2979fa..b6a630b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8857,25 +8857,6 @@
     field public java.lang.String targetPackage;
   }
 
-  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
-    ctor public IntentFilterVerificationInfo();
-    ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
-    ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    ctor public IntentFilterVerificationInfo(android.os.Parcel);
-    method public int describeContents();
-    method public java.lang.String[] getDomains();
-    method public java.lang.String getDomainsString();
-    method public java.lang.String getPackageName();
-    method public int getStatus();
-    method public java.lang.String getStatusString();
-    method public static java.lang.String getStatusStringFromValue(int);
-    method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void setStatus(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
-    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
-  }
-
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9104,7 +9085,6 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -30655,6 +30635,7 @@
     method public android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.graphics.drawable.Drawable getActivityLogo(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.graphics.drawable.Drawable getActivityLogo(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
     method public java.util.List<android.content.pm.PermissionGroupInfo> getAllPermissionGroups(int);
     method public android.graphics.drawable.Drawable getApplicationBanner(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationBanner(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -30672,7 +30653,6 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 7e06899..318d1db 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9101,25 +9101,6 @@
     field public java.lang.String targetPackage;
   }
 
-  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
-    ctor public IntentFilterVerificationInfo();
-    ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
-    ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    ctor public IntentFilterVerificationInfo(android.os.Parcel);
-    method public int describeContents();
-    method public java.lang.String[] getDomains();
-    method public java.lang.String getDomainsString();
-    method public java.lang.String getPackageName();
-    method public int getStatus();
-    method public java.lang.String getStatusString();
-    method public static java.lang.String getStatusStringFromValue(int);
-    method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void setStatus(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
-    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
-  }
-
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9354,7 +9335,6 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -33202,6 +33182,7 @@
     method public android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.graphics.drawable.Drawable getActivityLogo(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.graphics.drawable.Drawable getActivityLogo(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
     method public java.util.List<android.content.pm.PermissionGroupInfo> getAllPermissionGroups(int);
     method public android.graphics.drawable.Drawable getApplicationBanner(android.content.pm.ApplicationInfo);
     method public android.graphics.drawable.Drawable getApplicationBanner(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -33219,7 +33200,6 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 10dcd85..ffdc81d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1349,6 +1349,16 @@
     }
 
     @Override
+    public List<IntentFilter> getAllIntentFilters(String packageName) {
+        try {
+            return mPM.getAllIntentFilters(packageName);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return null;
+        }
+    }
+
+    @Override
     public void setInstallerPackageName(String targetPackage,
             String installerPackageName) {
         try {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eed0df5..55c990f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -448,6 +448,7 @@
     int getIntentVerificationStatus(String packageName, int userId);
     boolean updateIntentVerificationStatus(String packageName, int status, int userId);
     List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
+    List<IntentFilter> getAllIntentFilters(String packageName);
 
     VerifierDeviceIdentity getVerifierDeviceIdentity();
 
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index 60cb4a8..28cbaa8 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import com.android.internal.util.XmlUtils;
 import org.xmlpull.v1.XmlPullParser;
@@ -47,17 +48,17 @@
     private static final String ATTR_PACKAGE_NAME = "packageName";
     private static final String ATTR_STATUS = "status";
 
-    private String[] mDomains;
+    private ArrayList<String> mDomains;
     private String mPackageName;
     private int mMainStatus;
 
     public IntentFilterVerificationInfo() {
         mPackageName = null;
-        mDomains = new String[0];
+        mDomains = new ArrayList<>();
         mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     }
 
-    public IntentFilterVerificationInfo(String packageName, String[] domains) {
+    public IntentFilterVerificationInfo(String packageName, ArrayList<String> domains) {
         mPackageName = packageName;
         mDomains = domains;
         mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
@@ -72,10 +73,14 @@
         readFromParcel(source);
     }
 
-    public String[] getDomains() {
+    public ArrayList<String> getDomains() {
         return mDomains;
     }
 
+    public ArraySet<String> getDomainsSet() {
+        return new ArraySet<>(mDomains);
+    }
+
     public String getPackageName() {
         return mPackageName;
     }
@@ -140,7 +145,7 @@
         }
         mMainStatus = status;
 
-        ArrayList<String> list = new ArrayList<>();
+        mDomains = new ArrayList<>();
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -155,18 +160,13 @@
             if (tagName.equals(TAG_DOMAIN)) {
                 String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
                 if (!TextUtils.isEmpty(name)) {
-                    if (list == null) {
-                        list = new ArrayList<>();
-                    }
-                    list.add(name);
+                    mDomains.add(name);
                 }
             } else {
                 Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
             }
             XmlUtils.skipCurrentTag(parser);
         }
-
-        mDomains = list.toArray(new String[list.size()]);
     }
 
     public void writeToXml(XmlSerializer serializer) throws IOException {
@@ -201,14 +201,15 @@
     private void readFromParcel(Parcel source) {
         mPackageName = source.readString();
         mMainStatus = source.readInt();
-        mDomains = source.readStringArray();
+        mDomains = new ArrayList<>();
+        source.readStringList(mDomains);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mPackageName);
         dest.writeInt(mMainStatus);
-        dest.writeStringArray(mDomains);
+        dest.writeStringList(mDomains);
     }
 
     public static final Creator<IntentFilterVerificationInfo> CREATOR =
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4c99d09..303b709 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3635,13 +3635,29 @@
      * @param packageName the package name. When this parameter is set to a non null value,
      *                    the results will be filtered by the package name provided.
      *                    Otherwise, there will be no filtering and it will return a list
-     *                    corresponding for all packages for the provided userId.
-     * @return a list of IntentFilterVerificationInfo for a specific package and User.
+     *                    corresponding for all packages
+     *
+     * @return a list of IntentFilterVerificationInfo for a specific package.
+     *
+     * @hide
      */
     public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
             String packageName);
 
     /**
+     * Get the list of IntentFilter for a specific package.
+     *
+     * @param packageName the package name. This parameter is set to a non null value,
+     *                    the list will contain all the IntentFilter for that package.
+     *                    Otherwise, the list will be empty.
+     *
+     * @return a list of IntentFilter for a specific package.
+     *
+     * @hide
+     */
+    public abstract List<IntentFilter> getAllIntentFilters(String packageName);
+
+    /**
      * Change the installer associated with a given package.  There are limitations
      * on how the installer package can be changed; in particular:
      * <ul>
diff --git a/core/java/android/inputmethodservice/ExtractEditLayout.java b/core/java/android/inputmethodservice/ExtractEditLayout.java
index 5696839..e902443 100644
--- a/core/java/android/inputmethodservice/ExtractEditLayout.java
+++ b/core/java/android/inputmethodservice/ExtractEditLayout.java
@@ -163,6 +163,8 @@
             mCallback.onDestroyActionMode(this);
             mCallback = null;
 
+            mMenu.close();
+
             mExtractActionButton.setVisibility(VISIBLE);
             mEditButton.setVisibility(INVISIBLE);
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 508fdee..3051926 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -269,6 +269,15 @@
         public abstract long getTotalTimeLocked(long elapsedRealtimeUs, int which);
 
         /**
+         * Returns the total time in microseconds associated with this Timer since the
+         * 'mark' was last set.
+         *
+         * @param elapsedRealtimeUs current elapsed realtime of system in microseconds
+         * @return a time in microseconds
+         */
+        public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs);
+
+        /**
          * Temporary for debugging.
          */
         public abstract void logState(Printer pw, String prefix);
@@ -332,7 +341,17 @@
          * @return a Map from Strings to Uid.Pkg objects.
          */
         public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
-        
+
+        /**
+         * Returns the time in milliseconds that this app kept the WiFi controller in the
+         * specified state <code>type</code>.
+         * @param type one of {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, or
+         *             {@link #CONTROLLER_TX_TIME}.
+         * @param which one of {@link #STATS_CURRENT}, {@link #STATS_SINCE_CHARGED}, or
+         *              {@link #STATS_SINCE_UNPLUGGED}.
+         */
+        public abstract long getWifiControllerActivity(int type, int which);
+
         /**
          * {@hide}
          */
@@ -1914,7 +1933,6 @@
     public static final int NETWORK_MOBILE_TX_DATA = 1;
     public static final int NETWORK_WIFI_RX_DATA = 2;
     public static final int NETWORK_WIFI_TX_DATA = 3;
-
     public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_TX_DATA + 1;
 
     public abstract long getNetworkActivityBytes(int type, int which);
@@ -1923,10 +1941,25 @@
     public static final int CONTROLLER_IDLE_TIME = 0;
     public static final int CONTROLLER_RX_TIME = 1;
     public static final int CONTROLLER_TX_TIME = 2;
-    public static final int CONTROLLER_ENERGY = 3;
-    public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_ENERGY + 1;
+    public static final int CONTROLLER_POWER_DRAIN = 3;
+    public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_POWER_DRAIN + 1;
 
+    /**
+     * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
+     * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
+     * respective state.
+     * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
+     * milli-ampere-milliseconds (mAms).
+     */
     public abstract long getBluetoothControllerActivity(int type, int which);
+
+    /**
+     * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
+     * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
+     * respective state.
+     * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
+     * milli-ampere-milliseconds (mAms).
+     */
     public abstract long getWifiControllerActivity(int type, int which);
 
     /**
@@ -2618,7 +2651,7 @@
                         label = "???";
                 }
                 dumpLine(pw, uid, category, POWER_USE_ITEM_DATA, label,
-                        BatteryStatsHelper.makemAh(bs.value));
+                        BatteryStatsHelper.makemAh(bs.totalPowerMah));
             }
         }
 
@@ -3264,6 +3297,13 @@
 
         sb.setLength(0);
         sb.append(prefix);
+        sb.append("  WiFi Energy use: ").append(BatteryStatsHelper.makemAh(
+                getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which) / (double)(1000*60*60)));
+        sb.append(" mAh");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
                 sb.append("  Bluetooth on: "); formatTimeMs(sb, bluetoothOnTime / 1000);
                 sb.append("("); sb.append(formatRatioLocked(bluetoothOnTime, whichBatteryRealtime));
                 sb.append(")");
@@ -3376,48 +3416,48 @@
                 final BatterySipper bs = sippers.get(i);
                 switch (bs.drainType) {
                     case IDLE:
-                        pw.print(prefix); pw.print("    Idle: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Idle: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case CELL:
-                        pw.print(prefix); pw.print("    Cell standby: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Cell standby: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case PHONE:
-                        pw.print(prefix); pw.print("    Phone calls: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Phone calls: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case WIFI:
-                        pw.print(prefix); pw.print("    Wifi: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Wifi: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case BLUETOOTH:
-                        pw.print(prefix); pw.print("    Bluetooth: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Bluetooth: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case SCREEN:
-                        pw.print(prefix); pw.print("    Screen: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Screen: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case FLASHLIGHT:
-                        pw.print(prefix); pw.print("    Flashlight: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Flashlight: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case APP:
                         pw.print(prefix); pw.print("    Uid ");
                         UserHandle.formatUid(pw, bs.uidObj.getUid());
-                        pw.print(": "); printmAh(pw, bs.value); pw.println();
+                        pw.print(": "); printmAh(pw, bs.totalPowerMah); pw.println();
                         break;
                     case USER:
                         pw.print(prefix); pw.print("    User "); pw.print(bs.userId);
-                        pw.print(": "); printmAh(pw, bs.value); pw.println();
+                        pw.print(": "); printmAh(pw, bs.totalPowerMah); pw.println();
                         break;
                     case UNACCOUNTED:
-                        pw.print(prefix); pw.print("    Unaccounted: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Unaccounted: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                     case OVERCOUNTED:
-                        pw.print(prefix); pw.print("    Over-counted: "); printmAh(pw, bs.value);
+                        pw.print(prefix); pw.print("    Over-counted: "); printmAh(pw, bs.totalPowerMah);
                         pw.println();
                         break;
                 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7ab5aaa..e7c4328 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2427,7 +2427,9 @@
 
     @Override
     public void onProvideVirtualAssistStructure(ViewAssistStructure structure) {
-        mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure);
+        super.onProvideVirtualAssistStructure(structure);
+        // TODO: enable when chromium backend lands.
+        // mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure);
     }
 
     /** @hide */
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 16353e8..205d35e 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1810,9 +1810,7 @@
         }
         if (mRefreshProgressRunnable != null) {
             removeCallbacks(mRefreshProgressRunnable);
-        }
-        if (mRefreshProgressRunnable != null && mRefreshIsPosted) {
-            removeCallbacks(mRefreshProgressRunnable);
+            mRefreshIsPosted = false;
         }
         if (mAccessibilityEventSender != null) {
             removeCallbacks(mAccessibilityEventSender);
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 52e1728..9571109 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -79,10 +79,10 @@
     // Transparent alpha level
     private static final int ALPHA_TRANSPARENT = 0;
 
-    private static final int HOURS_IN_DAY = 24;
-    private static final int MINUTES_IN_HOUR = 60;
-    private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_DAY;
-    private static final int DEGREES_FOR_ONE_MINUTE = 360 / MINUTES_IN_HOUR;
+    private static final int HOURS_IN_CIRCLE = 12;
+    private static final int MINUTES_IN_CIRCLE = 60;
+    private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_CIRCLE;
+    private static final int DEGREES_FOR_ONE_MINUTE = 360 / MINUTES_IN_CIRCLE;
 
     private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
     private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
@@ -536,7 +536,7 @@
     }
 
     private void setCurrentMinuteInternal(int minute, boolean callback) {
-        mSelectionDegrees[MINUTES] = (minute % MINUTES_IN_HOUR) * DEGREES_FOR_ONE_MINUTE;
+        mSelectionDegrees[MINUTES] = (minute % MINUTES_IN_CIRCLE) * DEGREES_FOR_ONE_MINUTE;
 
         invalidate();
 
@@ -1140,8 +1140,8 @@
 
                     // If the touched minute is closer to the current minute
                     // than it is to the snapped minute, return current.
-                    final int currentOffset = getCircularDiff(current, touched, MINUTES_IN_HOUR);
-                    final int snappedOffset = getCircularDiff(snapped, touched, MINUTES_IN_HOUR);
+                    final int currentOffset = getCircularDiff(current, touched, MINUTES_IN_CIRCLE);
+                    final int snappedOffset = getCircularDiff(snapped, touched, MINUTES_IN_CIRCLE);
                     final int minute;
                     if (currentOffset < snappedOffset) {
                         minute = current;
@@ -1181,7 +1181,7 @@
                 }
             } else {
                 final int current = getCurrentMinute();
-                for (int i = 0; i < MINUTES_IN_HOUR; i += MINUTE_INCREMENT) {
+                for (int i = 0; i < MINUTES_IN_CIRCLE; i += MINUTE_INCREMENT) {
                     virtualViewIds.add(makeId(TYPE_MINUTE, i));
 
                     // If the current minute falls between two increments,
@@ -1239,7 +1239,7 @@
                 if (value < current && nextValue > current) {
                     // The current value is between two snap values.
                     return makeId(type, current);
-                } else if (nextValue < MINUTES_IN_HOUR) {
+                } else if (nextValue < MINUTES_IN_CIRCLE) {
                     return makeId(type, nextValue);
                 }
             }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index bea4ece..1746bed 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -109,6 +109,7 @@
     void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
     void noteWifiMulticastEnabledFromSource(in WorkSource ws);
     void noteWifiMulticastDisabledFromSource(in WorkSource ws);
+    void noteWifiRadioPowerState(int powerState, long timestampNs);
     void noteNetworkInterfaceType(String iface, int type);
     void noteNetworkStatsEnabled();
     void noteDeviceIdleMode(boolean enabled, boolean fromActive, boolean fromMotion);
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 4cd959f..056b0aa 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,17 +23,25 @@
 public class BatterySipper implements Comparable<BatterySipper> {
     public int userId;
     public Uid uidObj;
-    public double value;
-    public double[] values;
+    public double totalPowerMah;
     public DrainType drainType;
 
-    // Measured in milliseconds.
-    public long usageTime;
-    public long cpuTime;
-    public long gpsTime;
-    public long wifiRunningTime;
-    public long cpuFgTime;
-    public long wakeLockTime;
+    /**
+     * Generic usage time in milliseconds.
+     */
+    public long usageTimeMs;
+
+    /**
+     * Generic power usage in mAh.
+     */
+    public double usagePowerMah;
+
+    // Subsystem usage times.
+    public long cpuTimeMs;
+    public long gpsTimeMs;
+    public long wifiRunningTimeMs;
+    public long cpuFgTimeMs;
+    public long wakeLockTimeMs;
 
     public long mobileRxPackets;
     public long mobileTxPackets;
@@ -52,12 +60,13 @@
     public String packageWithHighestDrain;
 
     // Measured in mAh (milli-ampere per hour).
-    public double wifiPower;
-    public double cpuPower;
-    public double wakeLockPower;
-    public double mobileRadioPower;
-    public double gpsPower;
-    public double sensorPower;
+    // These are included when summed.
+    public double wifiPowerMah;
+    public double cpuPowerMah;
+    public double wakeLockPowerMah;
+    public double mobileRadioPowerMah;
+    public double gpsPowerMah;
+    public double sensorPowerMah;
 
     public enum DrainType {
         IDLE,
@@ -73,17 +82,12 @@
         OVERCOUNTED
     }
 
-    public BatterySipper(DrainType drainType, Uid uid, double[] values) {
-        this.values = values;
-        if (values != null) value = values[0];
+    public BatterySipper(DrainType drainType, Uid uid, double value) {
+        this.totalPowerMah = value;
         this.drainType = drainType;
         uidObj = uid;
     }
 
-    public double[] getValues() {
-        return values;
-    }
-
     public void computeMobilemspp() {
         long packets = mobileRxPackets+mobileTxPackets;
         mobilemspp = packets > 0 ? (mobileActive / (double)packets) : 0;
@@ -101,7 +105,7 @@
             }
         }
         // Return the flipped value because we want the items in descending order
-        return Double.compare(other.value, value);
+        return Double.compare(other.totalPowerMah, totalPowerMah);
     }
 
     /**
@@ -123,11 +127,14 @@
      * Add stats from other to this BatterySipper.
      */
     public void add(BatterySipper other) {
-        cpuTime += other.cpuTime;
-        gpsTime += other.gpsTime;
-        wifiRunningTime += other.wifiRunningTime;
-        cpuFgTime += other.cpuFgTime;
-        wakeLockTime += other.wakeLockTime;
+        totalPowerMah += other.totalPowerMah;
+        usageTimeMs += other.usageTimeMs;
+        usagePowerMah += other.usagePowerMah;
+        cpuTimeMs += other.cpuTimeMs;
+        gpsTimeMs += other.gpsTimeMs;
+        wifiRunningTimeMs += other.wifiRunningTimeMs;
+        cpuFgTimeMs += other.cpuFgTimeMs;
+        wakeLockTimeMs += other.wakeLockTimeMs;
         mobileRxPackets += other.mobileRxPackets;
         mobileTxPackets += other.mobileTxPackets;
         mobileActive += other.mobileActive;
@@ -138,11 +145,20 @@
         mobileTxBytes += other.mobileTxBytes;
         wifiRxBytes += other.wifiRxBytes;
         wifiTxBytes += other.wifiTxBytes;
-        wifiPower += other.wifiPower;
-        gpsPower += other.gpsPower;
-        cpuPower += other.cpuPower;
-        sensorPower += other.sensorPower;
-        mobileRadioPower += other.mobileRadioPower;
-        wakeLockPower += other.wakeLockPower;
+        wifiPowerMah += other.wifiPowerMah;
+        gpsPowerMah += other.gpsPowerMah;
+        cpuPowerMah += other.cpuPowerMah;
+        sensorPowerMah += other.sensorPowerMah;
+        mobileRadioPowerMah += other.mobileRadioPowerMah;
+        wakeLockPowerMah += other.wakeLockPowerMah;
+    }
+
+    /**
+     * Sum all the powers and store the value into `value`.
+     * @return the sum of all the power in this BatterySipper.
+     */
+    public double sumPower() {
+        return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah
+                + mobileRadioPowerMah + wakeLockPowerMah;
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index d3611bf..024b7c5 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -16,17 +16,12 @@
 
 package com.android.internal.os;
 
-import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA;
-import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA;
-import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA;
-import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.Uid;
 import android.os.Bundle;
@@ -38,7 +33,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.telephony.SignalStrength;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
@@ -54,7 +48,6 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 
 /**
  * A helper class for retrieving the power usage information for all applications and services.
@@ -63,8 +56,7 @@
  * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
  */
 public final class BatteryStatsHelper {
-
-    private static final boolean DEBUG = false;
+    static final boolean DEBUG = false;
 
     private static final String TAG = BatteryStatsHelper.class.getSimpleName();
 
@@ -81,14 +73,24 @@
     private Intent mBatteryBroadcast;
     private PowerProfile mPowerProfile;
 
-    private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
-    private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
-    private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
-    private final SparseArray<List<BatterySipper>> mUserSippers
-            = new SparseArray<List<BatterySipper>>();
-    private final SparseArray<Double> mUserPower = new SparseArray<Double>();
+    /**
+     * List of apps using power.
+     */
+    private final List<BatterySipper> mUsageList = new ArrayList<>();
 
-    private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>();
+    /**
+     * List of apps using wifi power.
+     */
+    private final List<BatterySipper> mWifiSippers = new ArrayList<>();
+
+    /**
+     * List of apps using bluetooth power.
+     */
+    private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
+
+    private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
+
+    private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
 
     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
 
@@ -102,29 +104,50 @@
     long mChargeTimeRemaining;
 
     private long mStatsPeriod = 0;
+
+    // The largest entry by power.
     private double mMaxPower = 1;
+
+    // The largest real entry by power (not undercounted or overcounted).
     private double mMaxRealPower = 1;
+
+    // Total computed power.
     private double mComputedPower;
     private double mTotalPower;
-    private double mWifiPower;
-    private double mBluetoothPower;
     private double mMinDrainedPower;
     private double mMaxDrainedPower;
 
-    // How much the apps together have kept the mobile radio active.
-    private long mAppMobileActive;
+    PowerCalculator mCpuPowerCalculator;
+    PowerCalculator mWakelockPowerCalculator;
+    MobileRadioPowerCalculator mMobileRadioPowerCalculator;
+    PowerCalculator mWifiPowerCalculator;
+    PowerCalculator mBluetoothPowerCalculator;
+    PowerCalculator mSensorPowerCalculator;
 
-    // How much the apps together have left WIFI running.
-    private long mAppWifiRunning;
+    public static boolean checkWifiOnly(Context context) {
+        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+    }
+
+    public static boolean checkHasWifiPowerReporting(Context context, PowerProfile profile) {
+        WifiManager manager = context.getSystemService(WifiManager.class);
+        if (manager.isEnhancedPowerReportingSupported()) {
+            if (profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
+                    profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
+                    profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     public BatteryStatsHelper(Context context) {
         this(context, true);
     }
 
     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
-        mContext = context;
-        mCollectBatteryBroadcast = collectBatteryBroadcast;
-        mWifiOnly = checkWifiOnly(context);
+        this(context, collectBatteryBroadcast, checkWifiOnly(context));
     }
 
     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
@@ -133,12 +156,6 @@
         mWifiOnly = wifiOnly;
     }
 
-    public static boolean checkWifiOnly(Context context) {
-        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
-    }
-
     public void storeStatsHistoryInFile(String fname) {
         synchronized (sFileXfer) {
             File path = makeFilePath(mContext, fname);
@@ -260,7 +277,7 @@
      * Refreshes the power usage list.
      */
     public void refreshStats(int statsType, int asUser) {
-        SparseArray<UserHandle> users = new SparseArray<UserHandle>(1);
+        SparseArray<UserHandle> users = new SparseArray<>(1);
         users.put(asUser, new UserHandle(asUser));
         refreshStats(statsType, users);
     }
@@ -270,7 +287,7 @@
      */
     public void refreshStats(int statsType, List<UserHandle> asUsers) {
         final int n = asUsers.size();
-        SparseArray<UserHandle> users = new SparseArray<UserHandle>(n);
+        SparseArray<UserHandle> users = new SparseArray<>(n);
         for (int i = 0; i < n; ++i) {
             UserHandle userHandle = asUsers.get(i);
             users.put(userHandle.getIdentifier(), userHandle);
@@ -295,22 +312,52 @@
         mMaxRealPower = 0;
         mComputedPower = 0;
         mTotalPower = 0;
-        mWifiPower = 0;
-        mBluetoothPower = 0;
-        mAppMobileActive = 0;
-        mAppWifiRunning = 0;
 
         mUsageList.clear();
         mWifiSippers.clear();
         mBluetoothSippers.clear();
         mUserSippers.clear();
-        mUserPower.clear();
         mMobilemsppList.clear();
 
         if (mStats == null) {
             return;
         }
 
+        if (mCpuPowerCalculator == null) {
+            mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
+        }
+        mCpuPowerCalculator.reset();
+
+        if (mWakelockPowerCalculator == null) {
+            mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
+        }
+        mWakelockPowerCalculator.reset();
+
+        if (mMobileRadioPowerCalculator == null) {
+            mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
+        }
+        mMobileRadioPowerCalculator.reset(mStats);
+
+        if (mWifiPowerCalculator == null) {
+            if (checkHasWifiPowerReporting(mContext, mPowerProfile)) {
+                mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
+            } else {
+                mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
+            }
+        }
+        mWifiPowerCalculator.reset();
+
+        if (mBluetoothPowerCalculator == null) {
+            mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+        }
+        mBluetoothPowerCalculator.reset();
+
+        if (mSensorPowerCalculator == null) {
+            mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
+                    (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
+        }
+        mSensorPowerCalculator.reset();
+
         mStatsType = statsType;
         mRawUptime = rawUptimeUs;
         mRawRealtime = rawRealtimeUs;
@@ -358,383 +405,113 @@
         Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
             @Override
             public int compare(BatterySipper lhs, BatterySipper rhs) {
-                if (lhs.mobilemspp < rhs.mobilemspp) {
-                    return 1;
-                } else if (lhs.mobilemspp > rhs.mobilemspp) {
-                    return -1;
-                }
-                return 0;
+                return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
             }
         });
 
         processMiscUsage();
 
+        Collections.sort(mUsageList);
+
+        // At this point, we've sorted the list so we are guaranteed the max values are at the top.
+        // We have only added real powers so far.
+        if (!mUsageList.isEmpty()) {
+            mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
+            final int usageListCount = mUsageList.size();
+            for (int i = 0; i < usageListCount; i++) {
+                mComputedPower += mUsageList.get(i).totalPowerMah;
+            }
+        }
+
         if (DEBUG) {
             Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
                     + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
         }
+
         mTotalPower = mComputedPower;
         if (mStats.getLowDischargeAmountSinceCharge() > 1) {
             if (mMinDrainedPower > mComputedPower) {
                 double amount = mMinDrainedPower - mComputedPower;
                 mTotalPower = mMinDrainedPower;
-                addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
+                BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
+
+                // Insert the BatterySipper in its sorted position.
+                int index = Collections.binarySearch(mUsageList, bs);
+                if (index < 0) {
+                    index = -(index + 1);
+                }
+                mUsageList.add(index, bs);
+                mMaxPower = Math.max(mMaxPower, amount);
             } else if (mMaxDrainedPower < mComputedPower) {
                 double amount = mComputedPower - mMaxDrainedPower;
-                addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
+
+                // Insert the BatterySipper in its sorted position.
+                BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
+                int index = Collections.binarySearch(mUsageList, bs);
+                if (index < 0) {
+                    index = -(index + 1);
+                }
+                mUsageList.add(index, bs);
+                mMaxPower = Math.max(mMaxPower, amount);
             }
         }
-
-        Collections.sort(mUsageList);
     }
 
     private void processAppUsage(SparseArray<UserHandle> asUsers) {
         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
-        final SensorManager sensorManager =
-                (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
-        final int which = mStatsType;
-        final int speedSteps = mPowerProfile.getNumSpeedSteps();
-        final double[] powerCpuNormal = new double[speedSteps];
-        final long[] cpuSpeedStepTimes = new long[speedSteps];
-        for (int p = 0; p < speedSteps; p++) {
-            powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
-        }
-        final double mobilePowerPerPacket = getMobilePowerPerPacket();
-        final double mobilePowerPerMs = getMobilePowerPerMs();
-        final double wifiPowerPerPacket = getWifiPowerPerPacket();
-        long totalAppWakelockTimeUs = 0;
-        BatterySipper osApp = null;
         mStatsPeriod = mTypeBatteryRealtime;
 
-        final ArrayList<BatterySipper> appList = new ArrayList<>();
-
-        // Max values used to normalize later.
-        double maxWifiPower = 0;
-        double maxCpuPower = 0;
-        double maxWakeLockPower = 0;
-        double maxMobileRadioPower = 0;
-        double maxGpsPower = 0;
-        double maxSensorPower = 0;
-
         final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
         final int NU = uidStats.size();
         for (int iu = 0; iu < NU; iu++) {
             final Uid u = uidStats.valueAt(iu);
-            final BatterySipper app = new BatterySipper(
-                    BatterySipper.DrainType.APP, u, new double[]{0});
+            final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
 
-            final Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
-            if (processStats.size() > 0) {
-                // Process CPU time.
+            mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+            mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+            mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+            mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+            mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+            mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
 
-                // Keep track of the package with highest drain.
-                double highestDrain = 0;
-
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
-                        : processStats.entrySet()) {
-                    Uid.Proc ps = ent.getValue();
-                    app.cpuFgTime += ps.getForegroundTime(which);
-                    final long totalCpuTime = ps.getUserTime(which) + ps.getSystemTime(which);
-                    app.cpuTime += totalCpuTime;
-
-                    // Calculate the total CPU time spent at the various speed steps.
-                    long totalTimeAtSpeeds = 0;
-                    for (int step = 0; step < speedSteps; step++) {
-                        cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
-                        totalTimeAtSpeeds += cpuSpeedStepTimes[step];
-                    }
-                    totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
-
-                    // Then compute the ratio of time spent at each speed and figure out
-                    // the total power consumption.
-                    double cpuPower = 0;
-                    for (int step = 0; step < speedSteps; step++) {
-                        final double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
-                        final double cpuSpeedStepPower =
-                                ratio * totalCpuTime * powerCpuNormal[step];
-                        if (DEBUG && ratio != 0) {
-                            Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
-                                    + step + " ratio=" + makemAh(ratio) + " power="
-                                    + makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
-                        }
-                        cpuPower += cpuSpeedStepPower;
-                    }
-
-                    if (DEBUG && cpuPower != 0) {
-                        Log.d(TAG, String.format("process %s, cpu power=%s",
-                                ent.getKey(), makemAh(cpuPower / (60 * 60 * 1000))));
-                    }
-                    app.cpuPower += cpuPower;
-
-                    // Each App can have multiple packages and with multiple running processes.
-                    // Keep track of the package who's process has the highest drain.
-                    if (app.packageWithHighestDrain == null ||
-                            app.packageWithHighestDrain.startsWith("*")) {
-                        highestDrain = cpuPower;
-                        app.packageWithHighestDrain = ent.getKey();
-                    } else if (highestDrain < cpuPower && !ent.getKey().startsWith("*")) {
-                        highestDrain = cpuPower;
-                        app.packageWithHighestDrain = ent.getKey();
-                    }
-                }
-            }
-
-            // Ensure that the CPU times make sense.
-            if (app.cpuFgTime > app.cpuTime) {
-                if (DEBUG && app.cpuFgTime > app.cpuTime + 10000) {
-                    Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
-                }
-
-                // Statistics may not have been gathered yet.
-                app.cpuTime = app.cpuFgTime;
-            }
-
-            // Convert the CPU power to mAh
-            app.cpuPower /= (60 * 60 * 1000);
-            maxCpuPower = Math.max(maxCpuPower, app.cpuPower);
-
-            // Process wake lock usage
-            final Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
-                    u.getWakelockStats();
-            long wakeLockTimeUs = 0;
-            for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
-                    : wakelockStats.entrySet()) {
-                final Uid.Wakelock wakelock = wakelockEntry.getValue();
-
-                // Only care about partial wake locks since full wake locks
-                // are canceled when the user turns the screen off.
-                BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
-                if (timer != null) {
-                    wakeLockTimeUs += timer.getTotalTimeLocked(mRawRealtime, which);
-                }
-            }
-            app.wakeLockTime = wakeLockTimeUs / 1000; // convert to millis
-            totalAppWakelockTimeUs += wakeLockTimeUs;
-
-            // Add cost of holding a wake lock.
-            app.wakeLockPower = (app.wakeLockTime *
-                    mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60 * 60 * 1000);
-            if (DEBUG && app.wakeLockPower != 0) {
-                Log.d(TAG, "UID " + u.getUid() + ": wake "
-                        + app.wakeLockTime + " power=" + makemAh(app.wakeLockPower));
-            }
-            maxWakeLockPower = Math.max(maxWakeLockPower, app.wakeLockPower);
-
-            // Add cost of mobile traffic.
-            final long mobileActive = u.getMobileRadioActiveTime(mStatsType);
-            app.mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
-            app.mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
-            app.mobileActive = mobileActive / 1000;
-            app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);
-            app.mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType);
-            app.mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType);
-
-            if (mobileActive > 0) {
-                // We are tracking when the radio is up, so can use the active time to
-                // determine power use.
-                mAppMobileActive += mobileActive;
-                app.mobileRadioPower = (mobilePowerPerMs * mobileActive) / 1000;
-            } else {
-                // We are not tracking when the radio is up, so must approximate power use
-                // based on the number of packets.
-                app.mobileRadioPower = (app.mobileRxPackets + app.mobileTxPackets)
-                        * mobilePowerPerPacket;
-            }
-            if (DEBUG && app.mobileRadioPower != 0) {
-                Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
-                        + (app.mobileRxPackets + app.mobileTxPackets)
-                        + " active time " + mobileActive
-                        + " power=" + makemAh(app.mobileRadioPower));
-            }
-            maxMobileRadioPower = Math.max(maxMobileRadioPower, app.mobileRadioPower);
-
-            // Add cost of wifi traffic
-            app.wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
-            app.wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
-            app.wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
-            app.wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
-
-            final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
-                    * wifiPowerPerPacket;
-            if (DEBUG && wifiPacketPower != 0) {
-                Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
-                        + (app.wifiRxPackets + app.wifiTxPackets)
-                        + " power=" + makemAh(wifiPacketPower));
-            }
-
-            // Add cost of keeping WIFI running.
-            app.wifiRunningTime = u.getWifiRunningTime(mRawRealtime, which) / 1000;
-            mAppWifiRunning += app.wifiRunningTime;
-
-            final double wifiLockPower = (app.wifiRunningTime
-                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60 * 60 * 1000);
-            if (DEBUG && wifiLockPower != 0) {
-                Log.d(TAG, "UID " + u.getUid() + ": wifi running "
-                        + app.wifiRunningTime + " power=" + makemAh(wifiLockPower));
-            }
-
-            // Add cost of WIFI scans
-            final long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;
-            final double wifiScanPower = (wifiScanTimeMs
-                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN))
-                    /  (60 * 60 * 1000);
-            if (DEBUG && wifiScanPower != 0) {
-                Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
-                        + " power=" + makemAh(wifiScanPower));
-            }
-
-            // Add cost of WIFI batch scans.
-            double wifiBatchScanPower = 0;
-            for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
-                final long batchScanTimeMs =
-                        u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;
-                final double batchScanPower = ((batchScanTimeMs
-                        * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
-                ) / (60 * 60 * 1000);
-                if (DEBUG && batchScanPower != 0) {
-                    Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
-                            + " time=" + batchScanTimeMs + " power=" + makemAh(batchScanPower));
-                }
-                wifiBatchScanPower += batchScanPower;
-            }
-
-            // Add up all the WiFi costs.
-            app.wifiPower = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
-            maxWifiPower = Math.max(maxWifiPower, app.wifiPower);
-
-            // Process Sensor usage
-            final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
-            final int NSE = sensorStats.size();
-            for (int ise = 0; ise < NSE; ise++) {
-                final Uid.Sensor sensor = sensorStats.valueAt(ise);
-                final int sensorHandle = sensorStats.keyAt(ise);
-                final BatteryStats.Timer timer = sensor.getSensorTime();
-                final long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000;
-                double sensorPower = 0;
-                switch (sensorHandle) {
-                    case Uid.Sensor.GPS:
-                        app.gpsTime = sensorTime;
-                        app.gpsPower = (app.gpsTime
-                                * mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON))
-                                / (60 * 60 * 1000);
-                        sensorPower = app.gpsPower;
-                        maxGpsPower = Math.max(maxGpsPower, app.gpsPower);
-                        break;
-                    default:
-                        List<Sensor> sensorList = sensorManager.getSensorList(
-                                android.hardware.Sensor.TYPE_ALL);
-                        for (android.hardware.Sensor s : sensorList) {
-                            if (s.getHandle() == sensorHandle) {
-                                sensorPower = (sensorTime * s.getPower()) / (60 * 60 * 1000);
-                                app.sensorPower += sensorPower;
-                                break;
-                            }
-                        }
-                }
-                if (DEBUG && sensorPower != 0) {
-                    Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
-                            + " time=" + sensorTime + " power=" + makemAh(sensorPower));
-                }
-            }
-            maxSensorPower = Math.max(maxSensorPower, app.sensorPower);
-
-            final double totalUnnormalizedPower = app.cpuPower + app.wifiPower + app.wakeLockPower
-                    + app.mobileRadioPower + app.gpsPower + app.sensorPower;
-            if (DEBUG && totalUnnormalizedPower != 0) {
-                Log.d(TAG, String.format("UID %d: total power=%s",
-                        u.getUid(), makemAh(totalUnnormalizedPower)));
+            final double totalPower = app.sumPower();
+            if (DEBUG && totalPower != 0) {
+                Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
+                        makemAh(totalPower)));
             }
 
             // Add the app to the list if it is consuming power.
-            if (totalUnnormalizedPower != 0 || u.getUid() == 0) {
-                appList.add(app);
-            }
-        }
-
-        // Fetch real power consumption from hardware.
-        double actualTotalWifiPower = 0.0;
-        if (mStats.getWifiControllerActivity(BatteryStats.CONTROLLER_ENERGY, mStatsType) != 0) {
-            final double kDefaultVoltage = 3.36;
-            final long energy = mStats.getWifiControllerActivity(
-                    BatteryStats.CONTROLLER_ENERGY, mStatsType);
-            final double voltage = mPowerProfile.getAveragePowerOrDefault(
-                    PowerProfile.OPERATING_VOLTAGE_WIFI, kDefaultVoltage);
-            actualTotalWifiPower = energy / (voltage * 1000*60*60);
-        }
-
-        final int appCount = appList.size();
-        for (int i = 0; i < appCount; i++) {
-            // Normalize power where possible.
-            final BatterySipper app = appList.get(i);
-            if (actualTotalWifiPower != 0) {
-                app.wifiPower = (app.wifiPower / maxWifiPower) * actualTotalWifiPower;
-            }
-
-            // Assign the final power consumption here.
-            final double power = app.wifiPower + app.cpuPower + app.wakeLockPower
-                    + app.mobileRadioPower + app.gpsPower + app.sensorPower;
-            app.values[0] = app.value = power;
-
-            //
-            // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
-            //
-
-            final int uid = app.getUid();
-            final int userId = UserHandle.getUserId(uid);
-            if (uid == Process.WIFI_UID) {
-                mWifiSippers.add(app);
-                mWifiPower += power;
-            } else if (uid == Process.BLUETOOTH_UID) {
-                mBluetoothSippers.add(app);
-                mBluetoothPower += power;
-            } else if (!forAllUsers && asUsers.get(userId) == null
-                    && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
-                // We are told to just report this user's apps as one large entry.
-                List<BatterySipper> list = mUserSippers.get(userId);
-                if (list == null) {
-                    list = new ArrayList<>();
-                    mUserSippers.put(userId, list);
-                }
-                list.add(app);
-
-                Double userPower = mUserPower.get(userId);
-                if (userPower == null) {
-                    userPower = power;
+            if (totalPower != 0 || u.getUid() == 0) {
+                //
+                // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
+                //
+                final int uid = app.getUid();
+                final int userId = UserHandle.getUserId(uid);
+                if (uid == Process.WIFI_UID) {
+                    mWifiSippers.add(app);
+                } else if (uid == Process.BLUETOOTH_UID) {
+                    mBluetoothSippers.add(app);
+                } else if (!forAllUsers && asUsers.get(userId) == null
+                        && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
+                    // We are told to just report this user's apps as one large entry.
+                    List<BatterySipper> list = mUserSippers.get(userId);
+                    if (list == null) {
+                        list = new ArrayList<>();
+                        mUserSippers.put(userId, list);
+                    }
+                    list.add(app);
                 } else {
-                    userPower += power;
+                    mUsageList.add(app);
                 }
-                mUserPower.put(userId, userPower);
-            } else {
-                mUsageList.add(app);
-                if (power > mMaxPower) mMaxPower = power;
-                if (power > mMaxRealPower) mMaxRealPower = power;
-                mComputedPower += power;
-            }
 
-            if (uid == 0) {
-                osApp = app;
-            }
-        }
-
-        // The device has probably been awake for longer than the screen on
-        // time and application wake lock time would account for.  Assign
-        // this remainder to the OS, if possible.
-        if (osApp != null) {
-            long wakeTimeMillis = mBatteryUptime / 1000;
-            wakeTimeMillis -= (totalAppWakelockTimeUs / 1000)
-                    + (mStats.getScreenOnTime(mRawRealtime, which) / 1000);
-            if (wakeTimeMillis > 0) {
-                double power = (wakeTimeMillis
-                        * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE))
-                        /  (60*60*1000);
-                if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
-                        + makemAh(power));
-                osApp.wakeLockTime += wakeTimeMillis;
-                osApp.value += power;
-                osApp.values[0] += power;
-                if (osApp.value > mMaxPower) mMaxPower = osApp.value;
-                if (osApp.value > mMaxRealPower) mMaxRealPower = osApp.value;
-                mComputedPower += power;
+                if (uid == 0) {
+                    // The device has probably been awake for longer than the screen on
+                    // time and application wake lock time would account for.  Assign
+                    // this remainder to the OS, if possible.
+                    mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime,
+                                                                mRawUptime, mStatsType);
+                    app.sumPower();
+                }
             }
         }
     }
@@ -744,7 +521,7 @@
         double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
                 * phoneOnTimeMs / (60*60*1000);
         if (phoneOnPower != 0) {
-            BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
+            addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
         }
     }
 
@@ -773,54 +550,19 @@
     }
 
     private void addRadioUsage() {
-        double power = 0;
-        final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
-        long signalTimeMs = 0;
-        long noCoverageTimeMs = 0;
-        for (int i = 0; i < BINS; i++) {
-            long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mRawRealtime, mStatsType)
-                    / 1000;
-            double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i))
-                        / (60*60*1000);
-            if (DEBUG && p != 0) {
-                Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
-                        + makemAh(p));
-            }
-            power += p;
-            signalTimeMs += strengthTimeMs;
-            if (i == 0) {
-                noCoverageTimeMs = strengthTimeMs;
-            }
-        }
-        long scanningTimeMs = mStats.getPhoneSignalScanningTime(mRawRealtime, mStatsType)
-                / 1000;
-        double p = (scanningTimeMs * mPowerProfile.getAveragePower(
-                        PowerProfile.POWER_RADIO_SCANNING))
-                        / (60*60*1000);
-        if (DEBUG && p != 0) {
-            Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p));
-        }
-        power += p;
-        long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType);
-        long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000;
-        if (remainingActiveTime > 0) {
-            power += getMobilePowerPerMs() * remainingActiveTime;
-        }
-        if (power != 0) {
-            BatterySipper bs =
-                    addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power);
-            if (signalTimeMs != 0) {
-                bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
-            }
-            bs.mobileActive = remainingActiveTime;
-            bs.mobileActiveCount = mStats.getMobileRadioActiveUnknownCount(mStatsType);
+        BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
+        mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime,
+                mStatsType);
+        radio.sumPower();
+        if (radio.totalPowerMah > 0) {
+            mUsageList.add(radio);
         }
     }
 
     private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
         for (int i=0; i<from.size(); i++) {
             BatterySipper wbs = from.get(i);
-            if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
+            if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
             bs.add(wbs);
         }
         bs.computeMobilemspp();
@@ -847,41 +589,12 @@
      * of WiFi to the WiFi subsystem.
      */
     private void addWiFiUsage() {
-        final long idleTimeMs = mStats.getWifiControllerActivity(
-                BatteryStats.CONTROLLER_IDLE_TIME, mStatsType);
-        final long txTimeMs = mStats.getWifiControllerActivity(
-                BatteryStats.CONTROLLER_TX_TIME, mStatsType);
-        final long rxTimeMs = mStats.getWifiControllerActivity(
-                BatteryStats.CONTROLLER_RX_TIME, mStatsType);
-        final long energy = mStats.getWifiControllerActivity(
-                BatteryStats.CONTROLLER_ENERGY, mStatsType);
-        final long totalTimeRunning = idleTimeMs + txTimeMs + rxTimeMs;
-
-        double powerDrain = 0;
-        if (energy == 0 && totalTimeRunning > 0) {
-            // Energy is not reported, which means we may have left over power drain not attributed
-            // to any app. Assign this power to the WiFi app.
-            // TODO(adamlesinski): This mimics the old behavior. However, mAppWifiRunningTime
-            // is the accumulation of the time each app kept the WiFi chip on. Multiple apps
-            // can do this at the same time, so these times do not add up to the total time
-            // the WiFi chip was on. Consider normalizing the time spent running and calculating
-            // power from that? Normalizing the times will assign a weight to each app which
-            // should better represent power usage.
-            powerDrain = ((totalTimeRunning - mAppWifiRunning)
-                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
-        }
-
-        if (DEBUG && powerDrain != 0) {
-            Log.d(TAG, "Wifi active: time=" + (txTimeMs + rxTimeMs)
-                    + " power=" + makemAh(powerDrain));
-        }
-
-        // TODO(adamlesinski): mWifiPower is already added as a BatterySipper...
-        // Are we double counting here?
-        final double power = mWifiPower + powerDrain;
-        if (power > 0) {
-            BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, totalTimeRunning, power);
+        BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
+        mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
+        bs.sumPower();
+        if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) {
             aggregateSippers(bs, mWifiSippers, "WIFI");
+            mUsageList.add(bs);
         }
     }
 
@@ -890,30 +603,10 @@
      * Bluetooth Category.
      */
     private void addBluetoothUsage() {
-        final double kDefaultVoltage = 3.36;
-        final long idleTimeMs = mStats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_IDLE_TIME, mStatsType);
-        final long txTimeMs = mStats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_TX_TIME, mStatsType);
-        final long rxTimeMs = mStats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_RX_TIME, mStatsType);
-        final long energy = mStats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_ENERGY, mStatsType);
-        final double voltage = mPowerProfile.getAveragePowerOrDefault(
-                PowerProfile.OPERATING_VOLTAGE_BLUETOOTH, kDefaultVoltage);
-
-        // energy is measured in mA * V * ms, and we are interested in mAh
-        final double powerDrain = energy / (voltage * 60*60*1000);
-
-        if (DEBUG && powerDrain != 0) {
-            Log.d(TAG, "Bluetooth active: time=" + (txTimeMs + rxTimeMs)
-                    + " power=" + makemAh(powerDrain));
-        }
-
-        final long totalTime = idleTimeMs + txTimeMs + rxTimeMs;
-        final double power = mBluetoothPower + powerDrain;
-        if (power > 0) {
-            BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, totalTime, power);
+        BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
+        mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
+                mStatsType);
+        if (bs.sumPower() > 0) {
             aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
         }
     }
@@ -928,55 +621,16 @@
     }
 
     private void addUserUsage() {
-        for (int i=0; i<mUserSippers.size(); i++) {
+        for (int i = 0; i < mUserSippers.size(); i++) {
             final int userId = mUserSippers.keyAt(i);
-            final List<BatterySipper> sippers = mUserSippers.valueAt(i);
-            Double userPower = mUserPower.get(userId);
-            double power = (userPower != null) ? userPower : 0.0;
-            BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power);
+            BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
             bs.userId = userId;
-            aggregateSippers(bs, sippers, "User");
+            aggregateSippers(bs, mUserSippers.valueAt(i), "User");
+            bs.sumPower();
+            mUsageList.add(bs);
         }
     }
 
-    /**
-     * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
-     */
-    private double getMobilePowerPerPacket() {
-        final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
-        final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
-                / 3600;
-
-        final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
-        final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
-        final long mobileData = mobileRx + mobileTx;
-
-        final long radioDataUptimeMs
-                = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType) / 1000;
-        final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
-                ? (mobileData / (double)radioDataUptimeMs)
-                : (((double)MOBILE_BPS) / 8 / 2048);
-
-        return (MOBILE_POWER / mobilePps) / (60*60);
-    }
-
-    /**
-     * Return estimated power (in mAs) of keeping the radio up
-     */
-    private double getMobilePowerPerMs() {
-        return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000);
-    }
-
-    /**
-     * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
-     */
-    private double getWifiPowerPerPacket() {
-        final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
-        final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
-                / 3600;
-        return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
-    }
-
     private void processMiscUsage() {
         addUserUsage();
         addPhoneUsage();
@@ -992,15 +646,10 @@
     }
 
     private BatterySipper addEntry(DrainType drainType, long time, double power) {
-        mComputedPower += power;
-        if (power > mMaxRealPower) mMaxRealPower = power;
-        return addEntryNoTotal(drainType, time, power);
-    }
-
-    private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
-        if (power > mMaxPower) mMaxPower = power;
-        BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});
-        bs.usageTime = time;
+        BatterySipper bs = new BatterySipper(drainType, null, 0);
+        bs.usagePowerMah = power;
+        bs.usageTimeMs = time;
+        bs.sumPower();
         mUsageList.add(bs);
         return bs;
     }
@@ -1015,7 +664,7 @@
 
     public long getStatsPeriod() { return mStatsPeriod; }
 
-    public int getStatsType() { return mStatsType; };
+    public int getStatsType() { return mStatsType; }
 
     public double getMaxPower() { return mMaxPower; }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 05ed3ab..793d0d3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -55,6 +55,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
@@ -95,6 +96,7 @@
 public final class BatteryStatsImpl extends BatteryStats {
     private static final String TAG = "BatteryStatsImpl";
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_ENERGY = false;
     private static final boolean DEBUG_HISTORY = false;
     private static final boolean USE_OLD_HISTORY = false;   // for debugging.
 
@@ -182,22 +184,20 @@
     // elapsed time by the number of active timers to arrive at that timer's share of the time.
     // In order to do this, we must refresh each timer whenever the number of active timers
     // changes.
-    final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<StopwatchTimer>();
-    final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<StopwatchTimer>();
-    final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<StopwatchTimer>();
-    final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers
-            = new SparseArray<ArrayList<StopwatchTimer>>();
-    final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<StopwatchTimer>();
-    final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<StopwatchTimer>();
-    final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<StopwatchTimer>();
-    final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
-    final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
-            new SparseArray<ArrayList<StopwatchTimer>>();
-    final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<StopwatchTimer>();
-    final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
+    final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
+    final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
+    final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
+    final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
 
     // Last partial timers we use for distributing CPU usage.
-    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
 
     // These are the objects that will want to do something when the device
     // is unplugged from power.
@@ -227,7 +227,7 @@
     final HistoryItem mHistoryLastLastWritten = new HistoryItem();
     final HistoryItem mHistoryReadTmp = new HistoryItem();
     final HistoryItem mHistoryAddTmp = new HistoryItem();
-    final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap();
+    final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
     String[] mReadHistoryStrings;
     int[] mReadHistoryUids;
     int mReadHistoryChars;
@@ -450,6 +450,8 @@
 
     private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
 
+    private PowerProfile mPowerProfile;
+
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
@@ -928,6 +930,12 @@
         long mUnpluggedTime;
 
         /**
+         * The total time this timer has been running until the latest mark has been set.
+         * Subtract this from mTotalTime to get the time spent running since the mark was set.
+         */
+        long mTimeBeforeMark;
+
+        /**
          * Constructs from a parcel.
          * @param type
          * @param timeBase
@@ -945,6 +953,7 @@
             mLoadedTime = in.readLong();
             mLastTime = 0;
             mUnpluggedTime = in.readLong();
+            mTimeBeforeMark = in.readLong();
             timeBase.add(this);
             if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
         }
@@ -964,7 +973,7 @@
          * so can be completely dropped.
          */
         boolean reset(boolean detachIfReset) {
-            mTotalTime = mLoadedTime = mLastTime = 0;
+            mTotalTime = mLoadedTime = mLastTime = mTimeBeforeMark = 0;
             mCount = mLoadedCount = mLastCount = 0;
             if (detachIfReset) {
                 detach();
@@ -985,8 +994,10 @@
             out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
             out.writeLong(mLoadedTime);
             out.writeLong(mUnpluggedTime);
+            out.writeLong(mTimeBeforeMark);
         }
 
+        @Override
         public void onTimeStarted(long elapsedRealtime, long timeBaseUptime, long baseRealtime) {
             if (DEBUG && mType < 0) {
                 Log.v(TAG, "unplug #" + mType + ": realtime=" + baseRealtime
@@ -1002,6 +1013,7 @@
             }
         }
 
+        @Override
         public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
             if (DEBUG && mType < 0) {
                 Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtime
@@ -1055,6 +1067,13 @@
             return val;
         }
 
+        @Override
+        public long getTimeSinceMarkLocked(long elapsedRealtimeUs) {
+            long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
+            return val - mTimeBeforeMark;
+        }
+
+        @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCount=" + mCount
                     + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
@@ -1080,6 +1099,9 @@
             mCount = mLoadedCount = in.readInt();
             mLastCount = 0;
             mUnpluggedCount = mCount;
+
+            // When reading the summary, we set the mark to be the latest information.
+            mTimeBeforeMark = mTotalTime;
         }
     }
 
@@ -1475,21 +1497,6 @@
             return mNesting > 0;
         }
 
-        long checkpointRunningLocked(long elapsedRealtimeMs) {
-            if (mNesting > 0) {
-                // We are running...
-                final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
-                if (mTimerPool != null) {
-                    return refreshTimersLocked(batteryRealtime, mTimerPool, this);
-                }
-                final long heldTime = batteryRealtime - mUpdateTime;
-                mUpdateTime = batteryRealtime;
-                mTotalTime += heldTime;
-                return heldTime;
-            }
-            return 0;
-        }
-
         void stopRunningLocked(long elapsedRealtimeMs) {
             // Ignore attempt to stop a timer that isn't running
             if (mNesting == 0) {
@@ -1567,6 +1574,7 @@
             return mCount;
         }
 
+        @Override
         boolean reset(boolean detachIfReset) {
             boolean canDetach = mNesting <= 0;
             super.reset(canDetach && detachIfReset);
@@ -1577,6 +1585,7 @@
             return canDetach;
         }
 
+        @Override
         void detach() {
             super.detach();
             if (mTimerPool != null) {
@@ -1584,10 +1593,31 @@
             }
         }
 
+        @Override
         void readSummaryFromParcelLocked(Parcel in) {
             super.readSummaryFromParcelLocked(in);
             mNesting = 0;
         }
+
+        /**
+         * Set the mark so that we can query later for the total time the timer has
+         * accumulated since this point. The timer can be running or not.
+         *
+         * @param elapsedRealtimeMs the current elapsed realtime in milliseconds.
+         */
+        public void setMark(long elapsedRealtimeMs) {
+            final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+            if (mNesting > 0) {
+                // We are running.
+                if (mTimerPool != null) {
+                    refreshTimersLocked(batteryRealtime, mTimerPool, this);
+                } else {
+                    mTotalTime += batteryRealtime - mUpdateTime;
+                    mUpdateTime = batteryRealtime;
+                }
+            }
+            mTimeBeforeMark = mTotalTime;
+        }
     }
 
     public abstract class OverflowArrayMap<T> {
@@ -3890,7 +3920,6 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            scheduleSyncExternalStatsLocked();
         }
         mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3906,7 +3935,6 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            scheduleSyncExternalStatsLocked();
         }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
     }
@@ -3964,7 +3992,6 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            scheduleSyncExternalStatsLocked();
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -3980,7 +4007,6 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            scheduleSyncExternalStatsLocked();
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
     }
@@ -4088,7 +4114,8 @@
         // 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.
-        updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+        final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
+        updateMobileRadioStateLocked(elapsedRealtimeMs);
         updateWifiStateLocked(null);
     }
 
@@ -4369,6 +4396,18 @@
         LongSamplingCounter mMobileRadioActiveCount;
 
         /**
+         * The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode.
+         */
+        LongSamplingCounter[] mWifiControllerTime =
+                new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+
+        /**
+         * The amount of time this uid has kept the Bluetooth controller in idle, tx, and rx mode.
+         */
+        LongSamplingCounter[] mBluetoothControllerTime =
+                new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+
+        /**
          * The CPU times we had at the last history details update.
          */
         long mLastStepUserTime;
@@ -4404,22 +4443,22 @@
         /**
          * The statistics we have collected for this uid's sensor activations.
          */
-        final SparseArray<Sensor> mSensorStats = new SparseArray<Sensor>();
+        final SparseArray<Sensor> mSensorStats = new SparseArray<>();
 
         /**
          * The statistics we have collected for this uid's processes.
          */
-        final ArrayMap<String, Proc> mProcessStats = new ArrayMap<String, Proc>();
+        final ArrayMap<String, Proc> mProcessStats = new ArrayMap<>();
 
         /**
          * The statistics we have collected for this uid's processes.
          */
-        final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<String, Pkg>();
+        final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<>();
 
         /**
          * The transient wake stats we have collected for this uid's pids.
          */
-        final SparseArray<Pid> mPids = new SparseArray<Pid>();
+        final SparseArray<Pid> mPids = new SparseArray<>();
 
         public Uid(int uid) {
             mUid = uid;
@@ -4580,6 +4619,13 @@
             }
         }
 
+        public void noteWifiControllerActivityLocked(int type, long timeMs) {
+            if (mWifiControllerTime[type] == null) {
+                mWifiControllerTime[type] = new LongSamplingCounter(mOnBatteryTimeBase);
+            }
+            mWifiControllerTime[type].addCountLocked(timeMs);
+        }
+
         public StopwatchTimer createAudioTurnedOnTimerLocked() {
             if (mAudioTurnedOnTimer == null) {
                 mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
@@ -4895,6 +4941,15 @@
                     ? (int)mMobileRadioActiveCount.getCountLocked(which) : 0;
         }
 
+        @Override
+        public long getWifiControllerActivity(int type, int which) {
+            if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES &&
+                    mWifiControllerTime[type] != null) {
+                return mWifiControllerTime[type].getCountLocked(which);
+            }
+            return 0;
+        }
+
         void initNetworkActivityLocked() {
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
             mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -4978,6 +5033,16 @@
                 mMobileRadioActiveCount.reset(false);
             }
 
+            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+                if (mWifiControllerTime[i] != null) {
+                    mWifiControllerTime[i].reset(false);
+                }
+
+                if (mBluetoothControllerTime[i] != null) {
+                    mBluetoothControllerTime[i].reset(false);
+                }
+            }
+
             final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
             for (int iw=wakeStats.size()-1; iw>=0; iw--) {
                 Wakelock wl = wakeStats.valueAt(iw);
@@ -5100,6 +5165,16 @@
                         mNetworkPacketActivityCounters[i].detach();
                     }
                 }
+
+                for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+                    if (mWifiControllerTime[i] != null) {
+                        mWifiControllerTime[i].detach();
+                    }
+
+                    if (mBluetoothControllerTime[i] != null) {
+                        mBluetoothControllerTime[i].detach();
+                    }
+                }
                 mPids.clear();
             }
 
@@ -5189,6 +5264,7 @@
             } else {
                 out.writeInt(0);
             }
+
             if (mAudioTurnedOnTimer != null) {
                 out.writeInt(1);
                 mAudioTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -5240,6 +5316,24 @@
             } else {
                 out.writeInt(0);
             }
+
+            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+                if (mWifiControllerTime[i] != null) {
+                    out.writeInt(1);
+                    mWifiControllerTime[i].writeToParcel(out);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+
+            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+                if (mBluetoothControllerTime[i] != null) {
+                    out.writeInt(1);
+                    mBluetoothControllerTime[i].writeToParcel(out);
+                } else {
+                    out.writeInt(0);
+                }
+            }
         }
 
         void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
@@ -5389,6 +5483,22 @@
                 mNetworkByteActivityCounters = null;
                 mNetworkPacketActivityCounters = null;
             }
+
+            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+                if (in.readInt() != 0) {
+                    mWifiControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+                } else {
+                    mWifiControllerTime[i] = null;
+                }
+            }
+
+            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
+                if (in.readInt() != 0) {
+                    mBluetoothControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+                } else {
+                    mBluetoothControllerTime[i] = null;
+                }
+            }
         }
 
         /**
@@ -6644,6 +6754,12 @@
         readFromParcel(p);
     }
 
+    public void setPowerProfile(PowerProfile profile) {
+        synchronized (this) {
+            mPowerProfile = profile;
+        }
+    }
+
     public void setCallback(BatteryCallback cb) {
         mCallback = cb;
     }
@@ -7367,9 +7483,12 @@
      * @param info The energy information from the WiFi controller.
      */
     public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) {
-        final NetworkStats delta;
+        final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
+        NetworkStats delta = null;
         try {
-            delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+            if (!ArrayUtils.isEmpty(mWifiIfaces)) {
+                delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+            }
         } catch (IOException e) {
             Slog.wtf(TAG, "Failed to get wifi network stats", e);
             return;
@@ -7379,14 +7498,19 @@
             return;
         }
 
+        SparseLongArray rxPackets = new SparseLongArray();
+        SparseLongArray txPackets = new SparseLongArray();
+        long totalTxPackets = 0;
+        long totalRxPackets = 0;
         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) {
+                if (DEBUG_ENERGY) {
                     Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
-                            + " tx=" + entry.txBytes);
+                            + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
+                            + " txPackets=" + entry.txPackets);
                 }
 
                 if (entry.rxBytes == 0 || entry.txBytes == 0) {
@@ -7398,6 +7522,13 @@
                         entry.rxPackets);
                 u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
                         entry.txPackets);
+                rxPackets.put(u.getUid(), entry.rxPackets);
+                txPackets.put(u.getUid(), entry.txPackets);
+
+                // Sum the total number of packets so that the Rx Power and Tx Power can
+                // be evenly distributed amongst the apps.
+                totalRxPackets += entry.rxPackets;
+                totalTxPackets += entry.txPackets;
 
                 mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
                         entry.rxBytes);
@@ -7411,6 +7542,119 @@
         }
 
         if (info != null) {
+            // Measured in mAms
+            final long txTimeMs = info.getControllerTxTimeMillis();
+            final long rxTimeMs = info.getControllerRxTimeMillis();
+            final long idleTimeMs = info.getControllerIdleTimeMillis();
+            final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
+
+            long leftOverRxTimeMs = rxTimeMs;
+
+            if (DEBUG_ENERGY) {
+                Slog.d(TAG, "------ BEGIN WiFi power blaming ------");
+                Slog.d(TAG, "  Tx Time:    " + txTimeMs + " ms");
+                Slog.d(TAG, "  Rx Time:    " + rxTimeMs + " ms");
+                Slog.d(TAG, "  Idle Time:  " + idleTimeMs + " ms");
+                Slog.d(TAG, "  Total Time: " + totalTimeMs + " ms");
+            }
+
+            long totalWifiLockTimeMs = 0;
+            long totalScanTimeMs = 0;
+
+            // On the first pass, collect some totals so that we can normalize power
+            // calculations if we need to.
+            final int uidStatsSize = mUidStats.size();
+            for (int i = 0; i < uidStatsSize; i++) {
+                final Uid uid = mUidStats.valueAt(i);
+
+                // Sum the total scan power for all apps.
+                totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked(
+                        elapsedRealtimeMs * 1000) / 1000;
+
+                // Sum the total time holding wifi lock for all apps.
+                totalWifiLockTimeMs += uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
+                        elapsedRealtimeMs * 1000) / 1000;
+            }
+
+            if (DEBUG_ENERGY && totalScanTimeMs > rxTimeMs) {
+                Slog.d(TAG, "  !Estimated scan time > Actual rx time (" + totalScanTimeMs + " ms > "
+                        + rxTimeMs + " ms). Normalizing scan time.");
+            }
+
+            // Actually assign and distribute power usage to apps.
+            for (int i = 0; i < uidStatsSize; i++) {
+                final Uid uid = mUidStats.valueAt(i);
+
+                long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
+                        elapsedRealtimeMs * 1000) / 1000;
+                if (scanTimeSinceMarkMs > 0) {
+                    // Set the new mark so that next time we get new data since this point.
+                    uid.mWifiScanTimer.setMark(elapsedRealtimeMs);
+
+                    if (totalScanTimeMs > rxTimeMs) {
+                        // Our total scan time is more than the reported Rx time.
+                        // This is possible because the cost of a scan is approximate.
+                        // Let's normalize the result so that we evenly blame each app
+                        // scanning.
+                        //
+                        // This means that we may have apps that received packets not be blamed
+                        // for this, but this is fine as scans are relatively more expensive.
+                        scanTimeSinceMarkMs = (rxTimeMs * scanTimeSinceMarkMs) / totalScanTimeMs;
+                    }
+
+                    if (DEBUG_ENERGY) {
+                        Slog.d(TAG, "  ScanTime for UID " + uid.getUid() + ": "
+                                + scanTimeSinceMarkMs + " ms)");
+                    }
+                    uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, scanTimeSinceMarkMs);
+                    leftOverRxTimeMs -= scanTimeSinceMarkMs;
+                }
+
+                // Distribute evenly the power consumed while Idle to each app holding a WiFi
+                // lock.
+                final long wifiLockTimeSinceMarkMs = uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
+                        elapsedRealtimeMs * 1000) / 1000;
+                if (wifiLockTimeSinceMarkMs > 0) {
+                    // Set the new mark so that next time we get new data since this point.
+                    uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs);
+
+                    final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs)
+                            / totalWifiLockTimeMs;
+                    if (DEBUG_ENERGY) {
+                        Slog.d(TAG, "  IdleTime for UID " + uid.getUid() + ": "
+                                + myIdleTimeMs + " ms");
+                    }
+                    uid.noteWifiControllerActivityLocked(CONTROLLER_IDLE_TIME, myIdleTimeMs);
+                }
+            }
+
+            if (DEBUG_ENERGY) {
+                Slog.d(TAG, "  New RxPower: " + leftOverRxTimeMs + " ms");
+            }
+
+            // Distribute the Tx power appropriately between all apps that transmitted packets.
+            for (int i = 0; i < txPackets.size(); i++) {
+                final Uid uid = getUidStatsLocked(txPackets.keyAt(i));
+                final long myTxTimeMs = (txPackets.valueAt(i) * txTimeMs) / totalTxPackets;
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "  TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
+                }
+                uid.noteWifiControllerActivityLocked(CONTROLLER_TX_TIME, myTxTimeMs);
+            }
+
+            // Distribute the remaining Rx power appropriately between all apps that received
+            // packets.
+            for (int i = 0; i < rxPackets.size(); i++) {
+                final Uid uid = getUidStatsLocked(rxPackets.keyAt(i));
+                final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs) / totalRxPackets;
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "  RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
+                }
+                uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, myRxTimeMs);
+            }
+
+            // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
+
             // Update WiFi controller stats.
             mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
                     info.getControllerRxTimeMillis());
@@ -7418,19 +7662,29 @@
                     info.getControllerTxTimeMillis());
             mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
-            mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
-                    info.getControllerEnergyUsed());
+
+            final double powerDrainMaMs;
+            if (mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) == 0) {
+                powerDrainMaMs = 0.0;
+            } else {
+                powerDrainMaMs = info.getControllerEnergyUsed()
+                        / mPowerProfile.getAveragePower(
+                        PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
+            }
+            mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked((long) powerDrainMaMs);
         }
     }
 
     /**
      * Distribute Cell radio energy info and network traffic to apps.
      */
-    public void updateMobileRadioStateLocked(long elapsedRealtimeMs) {
-        final NetworkStats delta;
-
+    public void updateMobileRadioStateLocked(final long elapsedRealtimeMs) {
+        NetworkStats delta = null;
         try {
-            delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+            if (!ArrayUtils.isEmpty(mMobileIfaces)) {
+                delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+            }
         } catch (IOException e) {
             Slog.wtf(TAG, "Failed to get mobile network stats", e);
             return;
@@ -7440,14 +7694,24 @@
             return;
         }
 
-        long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs);
+        long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
+                elapsedRealtimeMs * 1000);
+        mMobileRadioActivePerAppTimer.setMark(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;
+            if (entry.rxBytes == 0 || entry.txBytes == 0) {
+                continue;
+            }
+
+            if (DEBUG_ENERGY) {
+                Slog.d(TAG, "Mobile uid " + entry.uid + ": delta rx=" + entry.rxBytes
+                        + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
+                        + " txPackets=" + entry.txPackets);
+            }
 
             final Uid u = getUidStatsLocked(mapUid(entry.uid));
             u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
@@ -7488,14 +7752,14 @@
      * @param info The energy information from the bluetooth controller.
      */
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
-        if (info != null && mOnBatteryInternal) {
+        if (info != null && mOnBatteryInternal && false) {
             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(
+            mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
                     info.getControllerEnergyUsed());
         }
     }
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
new file mode 100644
index 0000000..3557209
--- /dev/null
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -0,0 +1,53 @@
+/*
+ * 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.BatteryStats;
+import android.util.Log;
+
+public class BluetoothPowerCalculator extends PowerCalculator {
+    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private static final String TAG = "BluetoothPowerCalculator";
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                             long rawUptimeUs, int statsType) {
+        // No per-app distribution yet.
+    }
+
+    @Override
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+                                   long rawUptimeUs, int statsType) {
+        final long idleTimeMs = stats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_IDLE_TIME, statsType);
+        final long txTimeMs = stats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_TX_TIME, statsType);
+        final long rxTimeMs = stats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_RX_TIME, statsType);
+        final long powerMaMs = stats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_POWER_DRAIN, statsType);
+        final double powerMah = powerMaMs / (double)(1000*60*60);
+        final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
+
+        if (DEBUG && powerMah != 0) {
+            Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
+                    + " power=" + BatteryStatsHelper.makemAh(powerMah));
+        }
+
+        app.usagePowerMah = powerMah;
+        app.usageTimeMs = totalTimeMs;
+    }
+}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
new file mode 100644
index 0000000..6c3f958
--- /dev/null
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -0,0 +1,113 @@
+/*
+ * 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.BatteryStats;
+import android.util.ArrayMap;
+import android.util.Log;
+
+public class CpuPowerCalculator extends PowerCalculator {
+    private static final String TAG = "CpuPowerCalculator";
+    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+
+    private final double[] mPowerCpuNormal;
+
+    /**
+     * Reusable array for calculations.
+     */
+    private final long[] mSpeedStepTimes;
+
+    public CpuPowerCalculator(PowerProfile profile) {
+        final int speedSteps = profile.getNumSpeedSteps();
+        mPowerCpuNormal = new double[speedSteps];
+        mSpeedStepTimes = new long[speedSteps];
+        for (int p = 0; p < speedSteps; p++) {
+            mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
+        }
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                             long rawUptimeUs, int statsType) {
+        final int speedSteps = mSpeedStepTimes.length;
+
+        // Keep track of the package with highest drain.
+        double highestDrain = 0;
+
+        final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+        final int processStatsCount = processStats.size();
+        for (int i = 0; i < processStatsCount; i++) {
+            final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
+            final String processName = processStats.keyAt(i);
+
+            app.cpuFgTimeMs += ps.getForegroundTime(statsType);
+            final long totalCpuTime = ps.getUserTime(statsType) + ps.getSystemTime(statsType);
+            app.cpuTimeMs += totalCpuTime;
+
+            // Calculate the total CPU time spent at the various speed steps.
+            long totalTimeAtSpeeds = 0;
+            for (int step = 0; step < speedSteps; step++) {
+                mSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, statsType);
+                totalTimeAtSpeeds += mSpeedStepTimes[step];
+            }
+            totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
+
+            // Then compute the ratio of time spent at each speed and figure out
+            // the total power consumption.
+            double cpuPower = 0;
+            for (int step = 0; step < speedSteps; step++) {
+                final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds;
+                final double cpuSpeedStepPower = ratio * totalCpuTime * mPowerCpuNormal[step];
+                if (DEBUG && ratio != 0) {
+                    Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
+                            + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
+                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
+                }
+                cpuPower += cpuSpeedStepPower;
+            }
+
+            if (DEBUG && cpuPower != 0) {
+                Log.d(TAG, String.format("process %s, cpu power=%s",
+                        processName, BatteryStatsHelper.makemAh(cpuPower / (60 * 60 * 1000))));
+            }
+            app.cpuPowerMah += cpuPower;
+
+            // Each App can have multiple packages and with multiple running processes.
+            // Keep track of the package who's process has the highest drain.
+            if (app.packageWithHighestDrain == null ||
+                    app.packageWithHighestDrain.startsWith("*")) {
+                highestDrain = cpuPower;
+                app.packageWithHighestDrain = processName;
+            } else if (highestDrain < cpuPower && !processName.startsWith("*")) {
+                highestDrain = cpuPower;
+                app.packageWithHighestDrain = processName;
+            }
+        }
+
+        // Ensure that the CPU times make sense.
+        if (app.cpuFgTimeMs > app.cpuTimeMs) {
+            if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) {
+                Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
+            }
+
+            // Statistics may not have been gathered yet.
+            app.cpuTimeMs = app.cpuFgTimeMs;
+        }
+
+        // Convert the CPU power to mAh
+        app.cpuPowerMah /= (60 * 60 * 1000);
+    }
+}
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
new file mode 100644
index 0000000..9711c3b
--- /dev/null
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -0,0 +1,147 @@
+/*
+ * 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.BatteryStats;
+import android.telephony.SignalStrength;
+import android.util.Log;
+
+public class MobileRadioPowerCalculator extends PowerCalculator {
+    private static final String TAG = "MobileRadioPowerController";
+    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private final double mPowerRadioOn;
+    private final double[] mPowerBins = new double[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
+    private final double mPowerScan;
+    private BatteryStats mStats;
+    private long mTotalAppMobileActiveMs = 0;
+
+    /**
+     * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
+     */
+    private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
+        final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
+        final double MOBILE_POWER = mPowerRadioOn / 3600;
+
+        final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+                statsType);
+        final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+                statsType);
+        final long mobileData = mobileRx + mobileTx;
+
+        final long radioDataUptimeMs =
+                mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
+        final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
+                ? (mobileData / (double)radioDataUptimeMs)
+                : (((double)MOBILE_BPS) / 8 / 2048);
+        return (MOBILE_POWER / mobilePps) / (60*60);
+    }
+
+    public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) {
+        mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE);
+        for (int i = 0; i < mPowerBins.length; i++) {
+            mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE, i);
+        }
+        mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING);
+        mStats = stats;
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                             long rawUptimeUs, int statsType) {
+        // Add cost of mobile traffic.
+        app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+                statsType);
+        app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+                statsType);
+        app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;
+        app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
+        app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
+                statsType);
+        app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
+                statsType);
+
+        if (app.mobileActive > 0) {
+            // We are tracking when the radio is up, so can use the active time to
+            // determine power use.
+            mTotalAppMobileActiveMs += app.mobileActive;
+            app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60);
+        } else {
+            // We are not tracking when the radio is up, so must approximate power use
+            // based on the number of packets.
+            app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets)
+                    * getMobilePowerPerPacket(rawRealtimeUs, statsType);
+        }
+        if (DEBUG && app.mobileRadioPowerMah != 0) {
+            Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
+                    + (app.mobileRxPackets + app.mobileTxPackets)
+                    + " active time " + app.mobileActive
+                    + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah));
+        }
+    }
+
+    @Override
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+                                   long rawUptimeUs, int statsType) {
+        double power = 0;
+        long signalTimeMs = 0;
+        long noCoverageTimeMs = 0;
+        for (int i = 0; i < mPowerBins.length; i++) {
+            long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)
+                    / 1000;
+            final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000);
+            if (DEBUG && p != 0) {
+                Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+                        + BatteryStatsHelper.makemAh(p));
+            }
+            power += p;
+            signalTimeMs += strengthTimeMs;
+            if (i == 0) {
+                noCoverageTimeMs = strengthTimeMs;
+            }
+        }
+
+        final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
+                / 1000;
+        final double p = (scanningTimeMs * mPowerScan) / (60*60*1000);
+        if (DEBUG && p != 0) {
+            Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs
+                    + " power=" + BatteryStatsHelper.makemAh(p));
+        }
+        power += p;
+        long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
+        long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
+        if (remainingActiveTimeMs > 0) {
+            power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60);
+        }
+
+        if (power != 0) {
+            app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
+            app.mobileActive = remainingActiveTimeMs;
+            app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);
+            app.mobileRadioPowerMah = power;
+        }
+    }
+
+    @Override
+    public void reset() {
+        mTotalAppMobileActiveMs = 0;
+    }
+
+    public void reset(BatteryStats stats) {
+        reset();
+        mStats = stats;
+    }
+}
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
new file mode 100644
index 0000000..cd69d68
--- /dev/null
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.BatteryStats;
+
+/**
+ * Calculates power use of a device subsystem for an app.
+ */
+public abstract class PowerCalculator {
+    /**
+     * Calculate the amount of power an app used for this subsystem.
+     * @param app The BatterySipper that represents the power use of an app.
+     * @param u The recorded stats for the app.
+     * @param rawRealtimeUs The raw system realtime in microseconds.
+     * @param rawUptimeUs The raw system uptime in microseconds.
+     * @param statsType The type of stats. Can be {@link BatteryStats#STATS_CURRENT},
+     *                  {@link BatteryStats#STATS_SINCE_CHARGED}, or
+     *                  {@link BatteryStats#STATS_SINCE_UNPLUGGED}.
+     */
+    public abstract void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                                      long rawUptimeUs, int statsType);
+
+    /**
+     * Calculate the remaining power that can not be attributed to an app.
+     * @param app The BatterySipper that will represent this remaining power.
+     * @param stats The BatteryStats object from which to retrieve data.
+     * @param rawRealtimeUs The raw system realtime in microseconds.
+     * @param rawUptimeUs The raw system uptime in microseconds.
+     * @param statsType The type of stats. Can be {@link BatteryStats#STATS_CURRENT},
+     *                  {@link BatteryStats#STATS_SINCE_CHARGED}, or
+     *                  {@link BatteryStats#STATS_SINCE_UNPLUGGED}.
+     */
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+                                   long rawUptimeUs, int statsType) {
+    }
+
+    /**
+     * Reset any state maintained in this calculator.
+     */
+    public void reset() {
+    }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 944eb5a..7e6706c 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -18,6 +18,7 @@
 
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 
 import com.android.internal.util.XmlUtils;
@@ -75,10 +76,21 @@
      */
     public static final String POWER_WIFI_ACTIVE = "wifi.active";
 
-    /**
-     * Operating voltage of the WiFi controller.
-     */
-    public static final String OPERATING_VOLTAGE_WIFI = "wifi.voltage";
+    //
+    // Updated power constants. These are not estimated, they are real world
+    // currents and voltages for the underlying bluetooth and wifi controllers.
+    //
+
+    public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
+    public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
+    public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
+    public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
+
+    public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
+    public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
+    public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";
+    public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
+            "bluetooth.controller.voltage";
 
     /**
      * Power consumption when GPS is on.
@@ -100,10 +112,6 @@
      */
     public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
 
-    /**
-     * Operating voltage of the Bluetooth controller.
-     */
-    public static final String OPERATING_VOLTAGE_BLUETOOTH = "bluetooth.voltage";
 
     /**
      * Power consumption when screen is on, not including the backlight power.
@@ -162,7 +170,7 @@
      */
     public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
 
-    static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
+    static final HashMap<String, Object> sPowerMap = new HashMap<>();
 
     private static final String TAG_DEVICE = "device";
     private static final String TAG_ITEM = "item";
@@ -180,7 +188,8 @@
 
     private void readPowerValuesFromXml(Context context) {
         int id = com.android.internal.R.xml.power_profile;
-        XmlResourceParser parser = context.getResources().getXml(id);
+        final Resources resources = context.getResources();
+        XmlResourceParser parser = resources.getXml(id);
         boolean parsingArray = false;
         ArrayList<Double> array = new ArrayList<Double>();
         String arrayName = null;
@@ -231,6 +240,36 @@
         } finally {
             parser.close();
         }
+
+        // Now collect other config variables.
+        int[] configResIds = new int[] {
+                com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
+                com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
+                com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
+                com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
+                com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
+                com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
+                com.android.internal.R.integer.config_wifi_tx_cur_ma,
+                com.android.internal.R.integer.config_wifi_operating_voltage_mv,
+        };
+
+        String[] configResIdKeys = new String[] {
+                POWER_BLUETOOTH_CONTROLLER_IDLE,
+                POWER_BLUETOOTH_CONTROLLER_RX,
+                POWER_BLUETOOTH_CONTROLLER_TX,
+                POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
+                POWER_WIFI_CONTROLLER_IDLE,
+                POWER_WIFI_CONTROLLER_RX,
+                POWER_WIFI_CONTROLLER_TX,
+                POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
+        };
+
+        for (int i = 0; i < configResIds.length; i++) {
+            int value = resources.getInteger(configResIds[i]);
+            if (value > 0) {
+                sPowerMap.put(configResIdKeys[i], (double) value);
+            }
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java
new file mode 100644
index 0000000..c98639b
--- /dev/null
+++ b/core/java/com/android/internal/os/SensorPowerCalculator.java
@@ -0,0 +1,63 @@
+/*
+ * 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.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.os.BatteryStats;
+import android.util.SparseArray;
+
+import java.util.List;
+
+public class SensorPowerCalculator extends PowerCalculator {
+    private final List<Sensor> mSensors;
+    private final double mGpsPowerOn;
+
+    public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {
+        mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+        mGpsPowerOn = profile.getAveragePower(PowerProfile.POWER_GPS_ON);
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                             long rawUptimeUs, int statsType) {
+        // Process Sensor usage
+        final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
+        final int NSE = sensorStats.size();
+        for (int ise = 0; ise < NSE; ise++) {
+            final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
+            final int sensorHandle = sensorStats.keyAt(ise);
+            final BatteryStats.Timer timer = sensor.getSensorTime();
+            final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
+            switch (sensorHandle) {
+                case BatteryStats.Uid.Sensor.GPS:
+                    app.gpsTimeMs = sensorTime;
+                    app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60);
+                    break;
+                default:
+                    final int sensorsCount = mSensors.size();
+                    for (int i = 0; i < sensorsCount; i++) {
+                        final Sensor s = mSensors.get(i);
+                        if (s.getHandle() == sensorHandle) {
+                            app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60);
+                            break;
+                        }
+                    }
+                    break;
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
new file mode 100644
index 0000000..7575010f
--- /dev/null
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -0,0 +1,81 @@
+/*
+ * 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.BatteryStats;
+import android.util.ArrayMap;
+import android.util.Log;
+
+public class WakelockPowerCalculator extends PowerCalculator {
+    private static final String TAG = "WakelockPowerCalculator";
+    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private final double mPowerWakelock;
+    private long mTotalAppWakelockTimeMs = 0;
+
+    public WakelockPowerCalculator(PowerProfile profile) {
+        mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawUptimeUs,
+                             long rawRealtimeUs, int statsType) {
+        long wakeLockTimeUs = 0;
+        final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
+                u.getWakelockStats();
+        final int wakelockStatsCount = wakelockStats.size();
+        for (int i = 0; i < wakelockStatsCount; i++) {
+            final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);
+
+            // Only care about partial wake locks since full wake locks
+            // are canceled when the user turns the screen off.
+            BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
+            if (timer != null) {
+                wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
+            }
+        }
+        app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis
+        mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
+
+        // Add cost of holding a wake lock.
+        app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);
+        if (DEBUG && app.wakeLockPowerMah != 0) {
+            Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs
+                    + " power=" + BatteryStatsHelper.makemAh(app.wakeLockPowerMah));
+        }
+    }
+
+    @Override
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+                                   long rawUptimeUs, int statsType) {
+        long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000;
+        wakeTimeMillis -= mTotalAppWakelockTimeMs
+                + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000);
+        if (wakeTimeMillis > 0) {
+            final double power = (wakeTimeMillis * mPowerWakelock) / (1000*60*60);
+            if (DEBUG) {
+                Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
+                        + BatteryStatsHelper.makemAh(power));
+            }
+            app.wakeLockTimeMs += wakeTimeMillis;
+            app.wakeLockPowerMah += power;
+        }
+    }
+
+    @Override
+    public void reset() {
+        mTotalAppWakelockTimeMs = 0;
+    }
+}
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
new file mode 100644
index 0000000..4e77f6b
--- /dev/null
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.BatteryStats;
+
+/**
+ * WiFi power calculator for when BatteryStats supports energy reporting
+ * from the WiFi controller.
+ */
+public class WifiPowerCalculator extends PowerCalculator {
+    private final double mIdleCurrentMa;
+    private final double mTxCurrentMa;
+    private final double mRxCurrentMa;
+    private double mTotalAppPowerDrain = 0;
+
+    public WifiPowerCalculator(PowerProfile profile) {
+        mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
+        mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
+        mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                             long rawUptimeUs, int statsType) {
+        final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
+                statsType);
+        final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);
+        final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);
+        app.wifiRunningTimeMs = idleTime + rxTime + txTime;
+        app.wifiPowerMah =
+                ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
+                / (1000*60*60);
+        mTotalAppPowerDrain += app.wifiPowerMah;
+
+        app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+                statsType);
+        app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+                statsType);
+        app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+                statsType);
+        app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+                statsType);
+    }
+
+    @Override
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+                                   long rawUptimeUs, int statsType) {
+        final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
+                statsType);
+        final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME,
+                statsType);
+        final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME,
+                statsType);
+        app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
+
+        double powerDrain = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
+                statsType) / (1000*60*60);
+        if (powerDrain == 0) {
+            // Some controllers do not report power drain, so we can calculate it here.
+            powerDrain = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
+                    + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
+        }
+        app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
+    }
+
+    @Override
+    public void reset() {
+        mTotalAppPowerDrain = 0;
+    }
+}
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
new file mode 100644
index 0000000..0172367
--- /dev/null
+++ b/core/java/com/android/internal/os/WifiPowerEstimator.java
@@ -0,0 +1,95 @@
+/*
+ * 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.BatteryStats;
+
+/**
+ * Estimates WiFi power usage based on timers in BatteryStats.
+ */
+public class WifiPowerEstimator extends PowerCalculator {
+    private final double mWifiPowerPerPacket;
+    private final double mWifiPowerOn;
+    private final double mWifiPowerScan;
+    private final double mWifiPowerBatchScan;
+    private long mTotalAppWifiRunningTimeMs = 0;
+
+    public WifiPowerEstimator(PowerProfile profile) {
+        mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
+        mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON);
+        mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN);
+        mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN);
+    }
+
+    /**
+     * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
+     */
+    private static double getWifiPowerPerPacket(PowerProfile profile) {
+        final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
+        final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
+                / 3600;
+        return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+                             long rawUptimeUs, int statsType) {
+        app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+                statsType);
+        app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+                statsType);
+        app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+                statsType);
+        app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+                statsType);
+
+        final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
+                * mWifiPowerPerPacket;
+
+        app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
+        mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
+        final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
+
+        final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType);
+        final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
+
+        double wifiBatchScanPower = 0;
+        for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+            final long batchScanTimeMs =
+                    u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
+            final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
+            wifiBatchScanPower += batchScanPower;
+        }
+
+        app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
+    }
+
+    @Override
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+                                   long rawUptimeUs, int statsType) {
+        final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType)
+                / 1000;
+        final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn)
+                / (1000*60*60);
+        app.wifiRunningTimeMs = totalRunningTimeMs;
+        app.wifiPowerMah = Math.max(0, powerDrain);
+    }
+
+    @Override
+    public void reset() {
+        mTotalAppWifiRunningTimeMs = 0;
+    }
+}
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index cfb65a5..52f086e 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -57,7 +57,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on March 2, 2015.
+<p style="clear:both"><em>Data collected during a 7-day period ending on April 6, 2015.
 <br/>Any versions with less than 0.1% distribution are not shown.</em>
 </p>
 
@@ -88,7 +88,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on March 2, 2015.
+<p style="clear:both"><em>Data collected during a 7-day period ending on April 6, 2015.
 
 <br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
 
@@ -108,8 +108,7 @@
 
 
 <img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A67.5%2C32.5&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
-
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A65.9%2C33.8%2C0.3&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
 
 <p>To declare which version of OpenGL ES your application requires, you should use the {@code
 android:glEsVersion} attribute of the <a
@@ -127,17 +126,21 @@
 </tr>
 <tr>
 <td>2.0</td>
-<td>67.5%</td>
+<td>65.9%</td>
 </tr>
 <tr>
 <td>3.0</td>
-<td>32.5%</td>
+<td>33.8%</td>
+</tr>
+<tr>
+<td>3.1</td>
+<td>0.3%</td>
 </tr>
 </table>
 
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on March 2, 2015</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on April 6, 2015</em></p>
 
 
 
@@ -155,7 +158,7 @@
 var VERSION_DATA =
 [
   {
-    "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop&chco=c4df9b%2C6fad0c&chd=t%3A0.4%2C6.9%2C5.9%2C42.6%2C40.9%2C3.3&chf=bg%2Cs%2C00000000&chs=500x250&cht=p",
+    "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop&chf=bg%2Cs%2C00000000&chd=t%3A0.4%2C6.4%2C5.7%2C40.7%2C41.4%2C5.4&chco=c4df9b%2C6fad0c&chs=500x250&cht=p",
     "data": [
       {
         "api": 8,
@@ -165,37 +168,42 @@
       {
         "api": 10,
         "name": "Gingerbread",
-        "perc": "6.9"
+        "perc": "6.4"
       },
       {
         "api": 15,
         "name": "Ice Cream Sandwich",
-        "perc": "5.9"
+        "perc": "5.7"
       },
       {
         "api": 16,
         "name": "Jelly Bean",
-        "perc": "17.3"
+        "perc": "16.5"
       },
       {
         "api": 17,
         "name": "Jelly Bean",
-        "perc": "19.4"
+        "perc": "18.6"
       },
       {
         "api": 18,
         "name": "Jelly Bean",
-        "perc": "5.9"
+        "perc": "5.6"
       },
       {
         "api": 19,
         "name": "KitKat",
-        "perc": "40.9"
+        "perc": "41.4"
       },
       {
         "api": 21,
         "name": "Lollipop",
-        "perc": "3.3"
+        "perc": "5.0"
+      },
+      {
+        "api": 22,
+        "name": "Lollipop",
+        "perc": "0.4"
       }
     ]
   }
@@ -208,29 +216,29 @@
     "data": {
       "Large": {
         "hdpi": "0.6",
-        "ldpi": "0.5",
-        "mdpi": "5.1",
-        "tvdpi": "2.3",
+        "ldpi": "0.4",
+        "mdpi": "4.8",
+        "tvdpi": "2.2",
         "xhdpi": "0.6"
       },
       "Normal": {
-        "hdpi": "38.7",
-        "mdpi": "8.4",
+        "hdpi": "39.3",
+        "mdpi": "8.1",
         "tvdpi": "0.1",
-        "xhdpi": "18.9",
-        "xxhdpi": "15.8"
+        "xhdpi": "19.5",
+        "xxhdpi": "15.9"
       },
       "Small": {
-        "ldpi": "4.6"
+        "ldpi": "4.4"
       },
       "Xlarge": {
         "hdpi": "0.3",
-        "mdpi": "3.5",
+        "mdpi": "3.2",
         "xhdpi": "0.6"
       }
     },
-    "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chco=c4df9b%2C6fad0c&chd=t%3A5.1%2C17.0%2C2.4%2C39.6%2C20.1%2C15.8&chf=bg%2Cs%2C00000000&chs=400x250&cht=p",
-    "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chco=c4df9b%2C6fad0c&chd=t%3A4.4%2C9.1%2C81.9%2C4.6&chf=bg%2Cs%2C00000000&chs=400x250&cht=p"
+    "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chf=bg%2Cs%2C00000000&chd=t%3A4.8%2C16.1%2C2.3%2C40.2%2C20.7%2C15.9&chco=c4df9b%2C6fad0c&chs=400x250&cht=p",
+    "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chf=bg%2Cs%2C00000000&chd=t%3A4.1%2C8.6%2C82.9%2C4.4&chco=c4df9b%2C6fad0c&chs=400x250&cht=p"
   }
 ];
 
@@ -312,6 +320,11 @@
     "api":21,
     "link":"<a href='/about/versions/android-5.0.html'>5.0</a>",
     "codename":"Lollipop"
+  },
+  {
+    "api":22,
+    "link":"<a href='/about/versions/android-5.1.html'>5.1</a>",
+    "codename":"Lollipop"
   }
 ];
 
diff --git a/docs/html/google/gcm/c2dm.jd b/docs/html/google/gcm/c2dm.jd
index d0f8c711..bc58e66 100644
--- a/docs/html/google/gcm/c2dm.jd
+++ b/docs/html/google/gcm/c2dm.jd
@@ -33,8 +33,8 @@
 </div>
 </div>
 
-<p>Android Cloud to Device Messaging (C2DM) was officially deprecated on June 26, 2012, and has been
- shut down completely as of April 1, 2015.  <strong>C2DM developers are strongly encouraged to move
+<p>Android Cloud to Device Messaging (C2DM) was officially deprecated on June 26, 2012, and will be
+ shut down completely as of July 30, 2015.  <strong>C2DM developers are strongly encouraged to move
   to Google Cloud Messaging (GCM)</strong>. GCM is the next generation of C2DM.</p>
 
 <p>This document is addressed to  C2DM developers who are moving to GCM. It describes the differences between GCM and C2DM, and explains how to migrate existing C2DM apps to GCM.</p>
diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index 3f71d04..e75235e 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -82,14 +82,6 @@
 <img src="{@docRoot}images/tools/sync-project.png" style="vertical-align:bottom;margin:0;height:19px" />
 in the toolbar.
   </li>
-  <li>Open your app's manifest file and add the following tag as a child of the <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code &lt;application>}</a>
-element:
-<pre>
-&lt;meta-data android:name="com.google.android.gms.version"
-           android:value="&#64;integer/google_play_services_version" />
-</pre>
-  </li>
 </ol>
 
 <p>You can now begin developing features with the
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index e3cacf9..eb58af4 100644
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -34,7 +34,7 @@
   <h2>See also</h2>
   <ol>
     <li><a href="{@docRoot}training/in-app-billing/index.html">Selling In-app Products</a></li>
-  </ol>  
+  </ol>
 </div>
 </div>
 
@@ -42,26 +42,26 @@
 
 <p class="note"><strong>Note:</strong> To see a complete implementation and learn how to test your application, see the <a href="{@docRoot}training/in-app-billing/index.html">Selling In-app Products</a> training class. The training class provides a complete sample In-app Billing application, including convenience classes to handle key tasks related to setting up your connection, sending billing requests and processing responses from Google Play, and managing background threading so that you can make In-app Billing calls from your main activity.</p>
 
-<p>Before you start, be sure that you read the <a href="{@docRoot}google/play/billing/billing_overview.html">In-app Billing Overview</a> to familiarize yourself with 
+<p>Before you start, be sure that you read the <a href="{@docRoot}google/play/billing/billing_overview.html">In-app Billing Overview</a> to familiarize yourself with
 concepts that will make it easier for you to implement In-app Billing.</p>
 
-<p>To implement In-app Billing in your application, you need to do the 
+<p>To implement In-app Billing in your application, you need to do the
 following:</p>
 <ol>
   <li>Add the In-app Billing library to your project.</li>
   <li>Update your {@code AndroidManifest.xml} file.</li>
-  <li>Create a {@code ServiceConnection} and bind it to 
+  <li>Create a {@code ServiceConnection} and bind it to
 {@code IInAppBillingService}.</li>
-  <li>Send In-app Billing requests from your application to 
+  <li>Send In-app Billing requests from your application to
 {@code IInAppBillingService}.</li>
   <li>Handle In-app Billing responses from Google Play.</li>
 </ol>
 
 <h2 id="billing-add-aidl">Adding the AIDL file to your project</h2>
 
-<p>{@code IInAppBillingService.aidl} is an Android Interface Definition 
-Language (AIDL) file that defines the interface to the In-app Billing Version 
-3 service. You will use this interface to make billing requests by invoking IPC 
+<p>{@code IInAppBillingService.aidl} is an Android Interface Definition
+Language (AIDL) file that defines the interface to the In-app Billing Version
+3 service. You will use this interface to make billing requests by invoking IPC
 method calls.</p>
 <p>To get the AIDL file:</p>
 <ol>
@@ -76,28 +76,28 @@
 <ol>
 <li>Copy the {@code IInAppBillingService.aidl} file to your Android project.
   <ul>
-  <li>If you are using Eclipse: 
+  <li>If you are using Eclipse:
      <ol type="a">
-        <li>If you are starting from an existing Android project, open the project 
-in Eclipse. If you are creating a new Android project from scratch, click 
-<strong>File</strong> &gt; <strong>New</strong> &gt; <strong>Android Application 
-Project</strong>, then follow the instructions in the <strong>New Android 
+        <li>If you are starting from an existing Android project, open the project
+in Eclipse. If you are creating a new Android project from scratch, click
+<strong>File</strong> &gt; <strong>New</strong> &gt; <strong>Android Application
+Project</strong>, then follow the instructions in the <strong>New Android
 Application</strong> wizard to create a new project in your workspace.</li>
-	<li>In the {@code /src} directory, click <strong>File</strong> &gt; 
+	<li>In the {@code /src} directory, click <strong>File</strong> &gt;
 <strong>New</strong> &gt; <strong>Package</strong>, then create a package named {@code com.android.vending.billing}.</li>
-	<li>Copy the {@code IInAppBillingService.aidl} file from {@code &lt;sdk&gt;/extras/google/play_billing/} and paste it into the {@code src/com.android.vending.billing/} 
+	<li>Copy the {@code IInAppBillingService.aidl} file from {@code &lt;sdk&gt;/extras/google/play_billing/} and paste it into the {@code src/com.android.vending.billing/}
 folder in your workspace.</li>
      </ol>
   </li>
-  <li>If you are developing in a non-Eclipse environment: Create the following 
-directory {@code /src/com/android/vending/billing} and copy the 
-{@code IInAppBillingService.aidl} file into this directory. Put the AIDL file 
+  <li>If you are developing in a non-Eclipse environment: Create the following
+directory {@code /src/com/android/vending/billing} and copy the
+{@code IInAppBillingService.aidl} file into this directory. Put the AIDL file
 into your project and use the Ant tool to build your project so that the
 <code>IInAppBillingService.java</code> file gets generated.</li>
   </ul>
 </li>
-<li>Build your application. You should see a generated file named 
-{@code IInAppBillingService.java} in the {@code /gen} directory of your 
+<li>Build your application. You should see a generated file named
+{@code IInAppBillingService.java} in the {@code /gen} directory of your
 project.</li>
 </ol>
 
@@ -135,7 +135,7 @@
    }
 
    &#64;Override
-   public void onServiceConnected(ComponentName name, 
+   public void onServiceConnected(ComponentName name,
       IBinder service) {
        mService = IInAppBillingService.Stub.asInterface(service);
    }
@@ -162,7 +162,7 @@
     super.onDestroy();
     if (mService != null) {
         unbindService(mServiceConn);
-    }	
+    }
 }
 </pre>
 
@@ -185,13 +185,13 @@
 </pre>
 <p>To retrieve this information from Google Play, call the {@code getSkuDetails} method on the In-app Billing Version 3 API, and pass the method the In-app Billing API version (“3”), the package name of your calling app, the purchase type (“inapp”), and the {@link android.os.Bundle} that you created.</p>
 <pre>
-Bundle skuDetails = mService.getSkuDetails(3, 
+Bundle skuDetails = mService.getSkuDetails(3,
    getPackageName(), "inapp", querySkus);
 </pre>
 <p>If the request is successful, the returned {@link android.os.Bundle}has a response code of {@code BILLING_RESPONSE_RESULT_OK} (0).</p>
 <p class="note"><strong>Warning:</strong> Do not call the {@code getSkuDetails} method on the main thread. Calling this method triggers a network request which could block your main thread.  Instead, create a separate thread and call the {@code getSkuDetails} method from inside that thread.</p>
 
-<p>To see all the possible response codes from Google Play, see <a href="{@docRoot}google/play/billing/billing_reference.html#billing-codes">In-app Billing Reference</a>.</p>  
+<p>To see all the possible response codes from Google Play, see <a href="{@docRoot}google/play/billing/billing_reference.html#billing-codes">In-app Billing Reference</a>.</p>
 
 <p>The query results are stored in a String ArrayList with key {@code DETAILS_LIST}.  The purchase information is stored in the String in JSON format. To see the types of product detail information that are returned, see <a href="{@docRoot}google/play/billing/billing_reference.html#getSkuDetails">In-app Billing Reference</a>.</p>
 
@@ -201,7 +201,7 @@
 if (response == 0) {
    ArrayList&lt;String&gt; responseList
       = skuDetails.getStringArrayList("DETAILS_LIST");
-   
+
    for (String thisResponse : responseList) {
       JSONObject object = new JSONObject(thisResponse);
       String sku = object.getString("productId");
@@ -232,12 +232,12 @@
    1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
    Integer.valueOf(0));
 </pre>
-<p>Google Play sends a response to your {@link android.app.PendingIntent} to the {@link android.app.Activity#onActivityResult onActivityResult} method of your application. The {@link android.app.Activity#onActivityResult onActivityResult} method will have a result code of {@code Activity.RESULT_OK} (1) or {@code Activity.RESULT_CANCELED} (0). To see the types of order information that is returned in the response {@link android.content.Intent}, see <a href="{@docRoot}google/play/billing/billing_reference.html#getBuyIntent">In-app Billing Reference</a>.</p> 
+<p>Google Play sends a response to your {@link android.app.PendingIntent} to the {@link android.app.Activity#onActivityResult onActivityResult} method of your application. The {@link android.app.Activity#onActivityResult onActivityResult} method will have a result code of {@code Activity.RESULT_OK} (1) or {@code Activity.RESULT_CANCELED} (0). To see the types of order information that is returned in the response {@link android.content.Intent}, see <a href="{@docRoot}google/play/billing/billing_reference.html#getBuyIntent">In-app Billing Reference</a>.</p>
 
 <p>The purchase data for the order is a String in JSON format that is mapped to the {@code INAPP_PURCHASE_DATA} key in the response {@link android.content.Intent}, for example:
 <pre>
-'{ 
-   "orderId":"12999763169054705758.1371079406387615", 
+'{
+   "orderId":"12999763169054705758.1371079406387615",
    "packageName":"com.example.app",
    "productId":"exampleSku",
    "purchaseTime":1345678900000,
@@ -259,17 +259,17 @@
 <p>Continuing from the previous example, you get the response code, purchase data, and signature from the response {@link android.content.Intent}.</p>
 <pre>
 &#64;Override
-protected void onActivityResult(int requestCode, int resultCode, Intent data) {	
-   if (requestCode == 1001) {    	
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+   if (requestCode == 1001) {
       int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
       String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
       String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
-        
+
       if (resultCode == RESULT_OK) {
          try {
             JSONObject jo = new JSONObject(purchaseData);
             String sku = jo.getString("productId");
-            alert("You have bought the " + sku + ". Excellent choice, 
+            alert("You have bought the " + sku + ". Excellent choice,
                adventurer!");
           }
           catch (JSONException e) {
@@ -298,45 +298,45 @@
    ArrayList&lt;String&gt;  purchaseDataList =
       ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
    ArrayList&lt;String&gt;  signatureList =
-      ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE");
-   String continuationToken = 
+      ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
+   String continuationToken =
       ownedItems.getString("INAPP_CONTINUATION_TOKEN");
-   
+
    for (int i = 0; i < purchaseDataList.size(); ++i) {
       String purchaseData = purchaseDataList.get(i);
       String signature = signatureList.get(i);
       String sku = ownedSkus.get(i);
-  
+
       // do something with this purchase information
       // e.g. display the updated list of products owned by user
-   } 
+   }
 
-   // if continuationToken != null, call getPurchases again 
+   // if continuationToken != null, call getPurchases again
    // and pass in the token to retrieve more items
 }
 
 </pre>
 
 <h3 id="Consume">Consuming a Purchase</h3>
-<p>You can use the In-app Billing Version 3 API to track the ownership of 
-purchased in-app products in Google Play. Once an in-app product is purchased, 
-it is considered to be "owned" and cannot be purchased from Google Play. You 
-must send a consumption request for the in-app product before Google Play makes 
+<p>You can use the In-app Billing Version 3 API to track the ownership of
+purchased in-app products in Google Play. Once an in-app product is purchased,
+it is considered to be "owned" and cannot be purchased from Google Play. You
+must send a consumption request for the in-app product before Google Play makes
 it available for purchase again.</p>
-<p class="caution"><strong>Important</strong>: Managed in-app products are 
+<p class="caution"><strong>Important</strong>: Managed in-app products are
 consumable, but subscriptions are not.</p>
-<p>How you use the consumption mechanism in your app is up to you. Typically, 
-you would implement consumption for in-app products with temporary benefits that 
-users may want to purchase multiple times (for example, in-game currency or 
-equipment). You would typically not want to implement consumption for in-app 
-products that are purchased once and provide a permanent effect (for example, 
+<p>How you use the consumption mechanism in your app is up to you. Typically,
+you would implement consumption for in-app products with temporary benefits that
+users may want to purchase multiple times (for example, in-game currency or
+equipment). You would typically not want to implement consumption for in-app
+products that are purchased once and provide a permanent effect (for example,
 a premium upgrade).</p>
-<p>To record a purchase consumption, send the {@code consumePurchase} method to 
-the In-app Billing service and pass in the {@code purchaseToken} String value 
-that identifies the purchase to be removed. The {@code purchaseToken} is part 
-of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google 
-Play service following a successful purchase request. In this example, you are 
-recording the consumption of a product that is identified with the 
+<p>To record a purchase consumption, send the {@code consumePurchase} method to
+the In-app Billing service and pass in the {@code purchaseToken} String value
+that identifies the purchase to be removed. The {@code purchaseToken} is part
+of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google
+Play service following a successful purchase request. In this example, you are
+recording the consumption of a product that is identified with the
 {@code purchaseToken} in the {@code token} variable.</p>
 <pre>
 int response = mService.consumePurchase(3, getPackageName(), token);
@@ -346,10 +346,10 @@
 <p class="note"><strong>Security Recommendation:</strong> You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.</p>
 
 <h3 id="Subs">Implementing Subscriptions</h3>
-<p>Launching a purchase flow for a subscription is similar to launching the 
-purchase flow for a product, with the exception that the product type must be set 
-to "subs". The purchase result is delivered to your Activity's 
-{@link android.app.Activity#onActivityResult onActivityResult} method, exactly 
+<p>Launching a purchase flow for a subscription is similar to launching the
+purchase flow for a product, with the exception that the product type must be set
+to "subs". The purchase result is delivered to your Activity's
+{@link android.app.Activity#onActivityResult onActivityResult} method, exactly
 as in the case of in-app products.</p>
 <pre>
 Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
@@ -363,39 +363,39 @@
        Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
 }
 </pre>
-<p>To query for active subscriptions, use the {@code getPurchases} method, again 
+<p>To query for active subscriptions, use the {@code getPurchases} method, again
 with the product type parameter set to "subs".</p>
 <pre>
 Bundle activeSubs = mService.getPurchases(3, "com.example.myapp",
                    "subs", continueToken);
 </pre>
-<p>The call returns a {@code Bundle} with all the active subscriptions owned by 
-the user. Once a subscription expires without renewal, it will no longer appear 
+<p>The call returns a {@code Bundle} with all the active subscriptions owned by
+the user. Once a subscription expires without renewal, it will no longer appear
 in the returned {@code Bundle}.</p>
 
 <h2 id="billing-security">Securing Your Application</h2>
 
-<p>To help ensure the integrity of the transaction information that is sent to 
-your application, Google Play signs the JSON string that contains the response 
-data for a purchase order. Google Play uses the private key that is associated 
-with your application in the Developer Console to create this signature. The 
+<p>To help ensure the integrity of the transaction information that is sent to
+your application, Google Play signs the JSON string that contains the response
+data for a purchase order. Google Play uses the private key that is associated
+with your application in the Developer Console to create this signature. The
 Developer Console generates an RSA key pair for each application.<p>
 
-<p class="note"><strong>Note:</strong>To find the public key portion of this key 
-pair, open your application's details in the Developer Console, then click on 
-<strong>Services & APIs</strong>, and look at the field titled 
+<p class="note"><strong>Note:</strong>To find the public key portion of this key
+pair, open your application's details in the Developer Console, then click on
+<strong>Services & APIs</strong>, and look at the field titled
 <strong>Your License Key for This Application</strong>.</p>
 
-<p>The Base64-encoded RSA public key generated by Google Play is in binary 
-encoded, X.509 subjectPublicKeyInfo DER SEQUENCE format. It is the same public 
+<p>The Base64-encoded RSA public key generated by Google Play is in binary
+encoded, X.509 subjectPublicKeyInfo DER SEQUENCE format. It is the same public
 key that is used with Google Play licensing.</p>
 
-<p>When your application receives this signed response you can 
-use the public key portion of your RSA key pair to verify the signature. 
-By performing signature verification you can detect responses that have 
-been tampered with or that have been spoofed. You can perform this signature 
-verification step in your application; however, if your application connects 
-to a secure remote server then we recommend that you perform the signature 
+<p>When your application receives this signed response you can
+use the public key portion of your RSA key pair to verify the signature.
+By performing signature verification you can detect responses that have
+been tampered with or that have been spoofed. You can perform this signature
+verification step in your application; however, if your application connects
+to a secure remote server then we recommend that you perform the signature
 verification on that server.</p>
 
 <p>For more information about best practices for security and design, see <a
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index a72faea..cc5c403 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -239,7 +239,7 @@
 }
 
 void JankTracker::dumpData(const ProfileData* data, int fd) {
-    dprintf(fd, "\nStats since: %lluns", data->statStartTime);
+    dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime);
     dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
     dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
             (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
diff --git a/packages/DocumentsUI/tests/AndroidManifest.xml b/packages/DocumentsUI/tests/AndroidManifest.xml
index 81a2889..a312427 100644
--- a/packages/DocumentsUI/tests/AndroidManifest.xml
+++ b/packages/DocumentsUI/tests/AndroidManifest.xml
@@ -4,6 +4,17 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
+        <provider
+            android:name="com.android.documentsui.StubProvider"
+            android:authorities="com.android.documentsui.stubprovider"
+            android:exported="true"
+            android:grantUriPermissions="true"
+            android:permission="android.permission.MANAGE_DOCUMENTS"
+            android:enabled="true">
+            <intent-filter>
+                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+            </intent-filter>
+       </provider>
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
index cdb6b33..7faa3ce 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
@@ -19,7 +19,7 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 import com.google.android.collect.Lists;
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
new file mode 100644
index 0000000..08c36de
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -0,0 +1,181 @@
+/*
+ * 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.documentsui;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.database.MatrixCursor;
+import android.graphics.Point;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+public class StubProvider extends DocumentsProvider {
+    private static int STORAGE_SIZE = 1024 * 1024;  // 1 MB.
+    private static final String TAG = "StubProvider";
+    private static final String MY_ROOT_ID = "myRoot";
+    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
+            Root.COLUMN_AVAILABLE_BYTES
+    };
+    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
+            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+    };
+
+    private String mRootDocumentId;
+    private HashMap<String, File> mStorage = new HashMap<String, File>();
+    private int mStorageUsedBytes;
+
+    @Override
+    public boolean onCreate() {
+        final File cacheDir = getContext().getCacheDir();
+        removeRecursively(cacheDir);
+        mRootDocumentId = getDocumentIdForFile(cacheDir);
+        mStorage.put(mRootDocumentId, cacheDir);
+        return true;
+    }
+
+    @Override
+    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION);
+        final RowBuilder row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
+        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE);
+        row.add(Root.COLUMN_TITLE, "Foobar SD 4GB");
+        row.add(Root.COLUMN_DOCUMENT_ID, mRootDocumentId);
+        row.add(Root.COLUMN_AVAILABLE_BYTES, STORAGE_SIZE - mStorageUsedBytes);
+        return result;
+    }
+
+    @Override
+    public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
+        final File file = mStorage.get(documentId);
+        if (file == null) {
+            throw new FileNotFoundException();
+        }
+        includeFile(result, file);
+        return result;
+    }
+
+    @Override
+    public String createDocument(String parentDocumentId, String mimeType, String displayName)
+            throws FileNotFoundException {
+        final File parentFile = mStorage.get(parentDocumentId);
+        if (parentFile == null || !parentFile.isDirectory()) {
+            throw new FileNotFoundException();
+        }
+        final File file = new File(parentFile, displayName);
+        if (mimeType.equals(Document.MIME_TYPE_DIR)) {
+            if (!file.mkdirs()) {
+                throw new FileNotFoundException();
+            }
+        } else {
+            try {
+                if (!file.createNewFile()) {
+                    throw new FileNotFoundException();
+                }
+            }
+            catch (IOException e) {
+                throw new FileNotFoundException();
+            }
+        }
+
+        final String documentId = getDocumentIdForFile(file);
+        mStorage.put(documentId, file);
+        return documentId;
+    }
+
+    @Override
+    public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
+            throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
+        final File parentFile = mStorage.get(parentDocumentId);
+        if (parentFile == null || parentFile.isFile()) {
+            throw new FileNotFoundException();
+        }
+        for (File file : parentFile.listFiles()) {
+            includeFile(result, file);
+        }
+        return result;
+    }
+
+    @Override
+    public Cursor queryRecentDocuments(String rootId, String[] projection)
+            throws FileNotFoundException {
+        throw new FileNotFoundException();
+    }
+
+    @Override
+    public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
+            throws FileNotFoundException {
+        final File file = mStorage.get(docId);
+        if (file == null || !file.isFile())
+            throw new FileNotFoundException();
+        // TODO: Simulate running out of storage.
+        return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
+    }
+
+    @Override
+    public AssetFileDescriptor openDocumentThumbnail(
+            String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
+        throw new FileNotFoundException();
+    }
+
+    private void includeFile(MatrixCursor result, File file) {
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, getDocumentIdForFile(file));
+        row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
+        row.add(Document.COLUMN_SIZE, file.length());
+        // TODO: Provide real mime type for files.
+        row.add(Document.COLUMN_MIME_TYPE, file.isDirectory() ? Document.MIME_TYPE_DIR : "application/octet-stream");
+        int flags = 0;
+        // TODO: Add support for renaming and deleting.
+        if (file.isDirectory()) {
+            flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+        } else {
+            flags |= Document.FLAG_SUPPORTS_WRITE;
+        }
+        row.add(Document.COLUMN_FLAGS, flags);
+        row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
+    }
+
+    private String getDocumentIdForFile(File file) {
+        return file.getAbsolutePath();
+    }
+
+    private void removeRecursively(File file) {
+        for (File childFile : file.listFiles()) {
+            if (childFile.isDirectory()) {
+                removeRecursively(childFile);
+            }
+            childFile.delete();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index dd28734..d8e732e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -748,6 +748,11 @@
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
 
+            if (isSecure()) {
+                Log.d(TAG, "current mode is SecurityMode, ignore hide keyguard");
+                return;
+            }
+
             mExternallyEnabled = enabled;
 
             if (!enabled && mShowing) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index eb394c3..1ac1c8a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1487,7 +1487,7 @@
                 NetworkCapabilities.TRANSPORT_WIFI)) {
             timeout = Settings.Global.getInt(mContext.getContentResolver(),
                                              Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
-                                             0);
+                                             5);
             type = ConnectivityManager.TYPE_WIFI;
         } else {
             // do not track any other networks
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7b542be..b5b62b4 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -210,6 +210,7 @@
 
     private boolean mMobileActivityFromRadio = false;
     private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+    private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
 
     private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
             new RemoteCallbackList<INetworkActivityListener>();
@@ -434,6 +435,16 @@
             }
         }
 
+        if (ConnectivityManager.isNetworkTypeWifi(type)) {
+            if (mLastPowerStateFromWifi != powerState) {
+                mLastPowerStateFromWifi = powerState;
+                try {
+                    getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
         boolean isActive = powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
                 || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 18ab3b4..607e09c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17467,7 +17467,8 @@
                                 fd = ParcelFileDescriptor.open(heapdumpFile,
                                         ParcelFileDescriptor.MODE_CREATE |
                                                 ParcelFileDescriptor.MODE_TRUNCATE |
-                                                ParcelFileDescriptor.MODE_READ_WRITE);
+                                                ParcelFileDescriptor.MODE_WRITE_ONLY |
+                                                ParcelFileDescriptor.MODE_APPEND);
                                 IApplicationThread thread = myProc.thread;
                                 if (thread != null) {
                                     try {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f8e9cbf..d08cddc 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -156,8 +156,7 @@
     static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
     static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
     static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
-    static final int CONTAINER_TASK_LIST_EMPTY_TIMEOUT = FIRST_SUPERVISOR_STACK_MSG + 12;
-    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 13;
+    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
 
     private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
@@ -3803,15 +3802,6 @@
                         }
                     }
                 } break;
-                case CONTAINER_TASK_LIST_EMPTY_TIMEOUT: {
-                    synchronized (mService) {
-                        Slog.w(TAG, "Timeout waiting for all activities in task to finish. " +
-                                msg.obj);
-                        final ActivityContainer container = (ActivityContainer) msg.obj;
-                        container.mStack.finishAllActivitiesLocked(true);
-                        container.onTaskListEmptyLocked();
-                    }
-                } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
                     synchronized (mService) {
                         ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
@@ -3916,10 +3906,6 @@
                 }
                 mContainerState = CONTAINER_STATE_FINISHING;
 
-                final Message msg =
-                        mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
-                mHandler.sendMessageDelayed(msg, 2000);
-
                 long origId = Binder.clearCallingIdentity();
                 try {
                     mStack.finishAllActivitiesLocked(false);
@@ -4039,7 +4025,6 @@
         }
 
         void onTaskListEmptyLocked() {
-            mHandler.removeMessages(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
             detachLocked();
             deleteActivityContainer(this);
             mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c8db3be..ac70d88 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
@@ -122,6 +123,7 @@
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
+        mStats.setPowerProfile(new PowerProfile(context));
     }
 
     /**
@@ -541,6 +543,15 @@
         }
     }
 
+    @Override
+    public void noteWifiRadioPowerState(int powerState, long tsNanos) {
+        enforceCallingPermission();
+
+        // There was a change in WiFi power state.
+        // Collect data now for the past activity.
+        mHandler.scheduleSync();
+    }
+
     public void noteWifiRunning(WorkSource ws) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -1095,13 +1106,29 @@
                 result.mTimestamp = info.getTimeStamp();
                 result.mStackState = info.getStackState();
                 result.mControllerTxTimeMs =
-                        info.getControllerTxTimeMillis()- mLastInfo.mControllerTxTimeMs;
+                        info.mControllerTxTimeMs - mLastInfo.mControllerTxTimeMs;
                 result.mControllerRxTimeMs =
-                        info.getControllerRxTimeMillis() - mLastInfo.mControllerRxTimeMs;
-                result.mControllerIdleTimeMs =
-                        info.getControllerIdleTimeMillis() - mLastInfo.mControllerIdleTimeMs;
+                        info.mControllerRxTimeMs - mLastInfo.mControllerRxTimeMs;
                 result.mControllerEnergyUsed =
-                        info.getControllerEnergyUsed() - mLastInfo.mControllerEnergyUsed;
+                        info.mControllerEnergyUsed - mLastInfo.mControllerEnergyUsed;
+
+                // WiFi calculates the idle time as a difference from the on time and the various
+                // Rx + Tx times. There seems to be some missing time there because this sometimes
+                // becomes negative. Just cap it at 0 and move on.
+                result.mControllerIdleTimeMs =
+                        Math.max(0, info.mControllerIdleTimeMs - mLastInfo.mControllerIdleTimeMs);
+
+                if (result.mControllerTxTimeMs < 0 ||
+                        result.mControllerRxTimeMs < 0) {
+                    // The stats were reset by the WiFi system (which is why our delta is negative).
+                    // Returns the unaltered stats.
+                    result.mControllerEnergyUsed = info.mControllerEnergyUsed;
+                    result.mControllerRxTimeMs = info.mControllerRxTimeMs;
+                    result.mControllerTxTimeMs = info.mControllerTxTimeMs;
+                    result.mControllerIdleTimeMs = info.mControllerIdleTimeMs;
+
+                    Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
+                }
                 mLastInfo = info;
                 return result;
             }
@@ -1133,6 +1160,12 @@
      */
     void updateExternalStats() {
         synchronized (mExternalStatsLock) {
+            if (mContext == null) {
+                // We haven't started yet (which means the BatteryStatsImpl object has
+                // no power profile. Don't consume data we can't compute yet.
+                return;
+            }
+
             final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked();
             final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
             synchronized (mStats) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4fea889..80a4351 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -564,14 +564,15 @@
 
                 ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
                 final int filterCount = filters.size();
+                ArraySet<String> domainsSet = new ArraySet<>();
                 for (int m=0; m<filterCount; m++) {
                     PackageParser.ActivityIntentInfo filter = filters.get(m);
-                    synchronized (mPackages) {
-                        modified = mSettings.createIntentFilterVerificationIfNeededLPw(
-                                packageName, filter.getHosts());
-                    }
+                    domainsSet.addAll(filter.getHostsList());
                 }
+                ArrayList<String> domainsList = new ArrayList<>(domainsSet);
                 synchronized (mPackages) {
+                    modified = mSettings.createIntentFilterVerificationIfNeededLPw(
+                            packageName, domainsList);
                     if (modified) {
                         scheduleWriteSettingsLocked();
                     }
@@ -582,7 +583,7 @@
         }
 
         private void sendVerificationRequest(int userId, int verificationId,
-                                             IntentFilterVerificationState ivs) {
+                IntentFilterVerificationState ivs) {
 
             Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
             verificationIntent.putExtra(
@@ -716,33 +717,33 @@
             }
             return ivs;
         }
+    }
 
-        private boolean hasValidHosts(ArrayList<String> hosts) {
-            if (hosts.size() == 0) {
-                Slog.d(TAG, "IntentFilter does not contain any data hosts");
+    private static boolean hasValidHosts(ArrayList<String> hosts) {
+        if (hosts.size() == 0) {
+            Slog.d(TAG, "IntentFilter does not contain any data hosts");
+            return false;
+        }
+        String hostEndBase = null;
+        for (String host : hosts) {
+            String[] hostParts = host.split("\\.");
+            // Should be at minimum a host like "example.com"
+            if (hostParts.length < 2) {
+                Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
                 return false;
             }
-            String hostEndBase = null;
-            for (String host : hosts) {
-                String[] hostParts = host.split("\\.");
-                // Should be at minimum a host like "example.com"
-                if (hostParts.length < 2) {
-                    Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
-                    return false;
-                }
-                // Verify that we have the same ending domain
-                int length = hostParts.length;
-                String hostEnd = hostParts[length - 1] + hostParts[length - 2];
-                if (hostEndBase == null) {
-                    hostEndBase = hostEnd;
-                }
-                if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
-                    Slog.d(TAG, "IntentFilter does not contain the same data domains");
-                    return false;
-                }
+            // Verify that we have the same ending domain
+            int length = hostParts.length;
+            String hostEnd = hostParts[length - 1] + hostParts[length - 2];
+            if (hostEndBase == null) {
+                hostEndBase = hostEnd;
             }
-            return true;
+            if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
+                Slog.d(TAG, "IntentFilter does not contain the same data domains");
+                return false;
+            }
         }
+        return true;
     }
 
     private IntentFilterVerifier mIntentFilterVerifier;
@@ -3895,8 +3896,7 @@
                 resolveInfo = queryCrossProfileIntents(
                         matchingFilters, intent, resolvedType, flags, userId);
 
-                // Check for results in the current profile. Adding GET_RESOLVED_FILTER flags
-                // as we need it later
+                // Check for results in the current profile.
                 List<ResolveInfo> result = mActivities.queryIntent(
                         intent, resolvedType, flags, userId);
                 if (resolveInfo != null) {
@@ -3905,7 +3905,7 @@
                 }
                 result = filterIfNotPrimaryUser(result, userId);
                 if (result.size() > 1) {
-                    return filterCandidatesWithDomainPreferedActivitiesLPw(result);
+                    return filterCandidatesWithDomainPreferedActivitiesLPr(result);
                 }
 
                 return result;
@@ -3939,42 +3939,33 @@
         return resolveInfos;
     }
 
-    private List<ResolveInfo> filterCandidatesWithDomainPreferedActivitiesLPw(
+    private List<ResolveInfo> filterCandidatesWithDomainPreferedActivitiesLPr(
             List<ResolveInfo> candidates) {
         if (DEBUG_PREFERRED) {
             Slog.v("TAG", "Filtering results with prefered activities. Candidates count: " +
                     candidates.size());
         }
         final int userId = UserHandle.getCallingUserId();
-        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(candidates);
+        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
         synchronized (mPackages) {
-            final int count = result.size();
-            for (int n = count-1; n >= 0; n--) {
-                ResolveInfo info = result.get(n);
-                if (!info.filterNeedsVerification) {
-                    continue;
-                }
+            final int count = candidates.size();
+            // First, try to use the domain prefered App
+            for (int n=0; n<count; n++) {
+                ResolveInfo info = candidates.get(n);
                 String packageName = info.activityInfo.packageName;
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps != null) {
                     // Try to get the status from User settings first
-                    int status = ps.getDomainVerificationStatusForUser(userId);
-                    // if none available, get the master status
-                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
-                        if (ps.getIntentFilterVerificationInfo() != null) {
-                            status = ps.getIntentFilterVerificationInfo().getStatus();
-                        }
-                    }
+                    int status = getDomainVerificationStatusLPr(ps, userId);
                     if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
-                        result.clear();
                         result.add(info);
-                        // We break the for loop as we are good to go
-                        break;
-                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
-                        result.remove(n);
                     }
                 }
             }
+            // There is not much we can do, add all candidates
+            if (result.size() == 0) {
+                result.addAll(candidates);
+            }
         }
         if (DEBUG_PREFERRED) {
             Slog.v("TAG", "Filtered results with prefered activities. New candidates count: " +
@@ -3983,6 +3974,17 @@
         return result;
     }
 
+    private int getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
+        int status = ps.getDomainVerificationStatusForUser(userId);
+        // if none available, get the master status
+        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+            if (ps.getIntentFilterVerificationInfo() != null) {
+                status = ps.getIntentFilterVerificationInfo().getStatus();
+            }
+        }
+        return status;
+    }
+
     private ResolveInfo querySkipCurrentProfileIntents(
             List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
             int flags, int sourceUserId) {
@@ -8980,6 +8982,28 @@
         }
     }
 
+    @Override
+    public List<IntentFilter> getAllIntentFilters(String packageName) {
+        if (TextUtils.isEmpty(packageName)) {
+            return Collections.<IntentFilter>emptyList();
+        }
+        synchronized (mPackages) {
+            PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null || pkg.activities == null) {
+                return Collections.<IntentFilter>emptyList();
+            }
+            final int count = pkg.activities.size();
+            ArrayList<IntentFilter> result = new ArrayList<>();
+            for (int n=0; n<count; n++) {
+                PackageParser.Activity activity = pkg.activities.get(n);
+                if (activity.intents != null || activity.intents.size() > 0) {
+                    result.addAll(activity.intents);
+                }
+            }
+            return result;
+        }
+    }
+
     /**
      * Get the "allow unknown sources" setting.
      *
@@ -11184,7 +11208,7 @@
     }
 
     private void verifyIntentFiltersIfNeeded(int userId, int verifierUid,
-                                             PackageParser.Package pkg) {
+            PackageParser.Package pkg) {
         int size = pkg.activities.size();
         if (size == 0) {
             Slog.d(TAG, "No activity, so no need to verify any IntentFilter!");
@@ -11196,6 +11220,8 @@
 
         final int verificationId = mIntentFilterVerificationToken++;
         int count = 0;
+        final String packageName = pkg.packageName;
+        ArrayList<String> allHosts = new ArrayList<>();
         synchronized (mPackages) {
             for (PackageParser.Activity a : pkg.activities) {
                 for (ActivityIntentInfo filter : a.intents) {
@@ -11204,10 +11230,14 @@
                     if (needFilterVerification && needNetworkVerificationLPr(filter)) {
                         Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString());
                         mIntentFilterVerifier.addOneIntentFilterVerification(
-                                verifierUid, userId, verificationId, filter, pkg.packageName);
+                                verifierUid, userId, verificationId, filter, packageName);
                         count++;
                     } else {
                         Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
+                        ArrayList<String> list = filter.getHostsList();
+                        if (hasValidHosts(list)) {
+                            allHosts.addAll(list);
+                        }
                     }
                 }
             }
@@ -11219,6 +11249,11 @@
                     + (count > 1 ? "s" : "") +  " for userId:" + userId + "!");
         } else {
             Slog.d(TAG, "No need to start any IntentFilter verification!");
+            if (allHosts.size() > 0 && hasDomainURLs(pkg) &&
+                    mSettings.createIntentFilterVerificationIfNeededLPw(
+                            packageName, allHosts)) {
+                scheduleWriteSettingsLocked();
+            }
         }
     }
 
@@ -11271,6 +11306,10 @@
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
+    private static boolean hasDomainURLs(PackageParser.Package pkg) {
+        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+    }
+
     private static boolean isSystemApp(PackageSetting ps) {
         return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6b7c35c..2e2053d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -22,6 +22,8 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.PACKAGE_INFO_GID;
@@ -964,7 +966,8 @@
     }
 
     /* package protected */
-    boolean createIntentFilterVerificationIfNeededLPw(String packageName, String[] domains) {
+    boolean createIntentFilterVerificationIfNeededLPw(String packageName,
+            ArrayList<String> domains) {
         PackageSetting ps = mPackages.get(packageName);
         if (ps == null) {
             Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
@@ -973,9 +976,9 @@
         if (ps.getIntentFilterVerificationInfo() == null) {
             IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(packageName, domains);
             ps.setIntentFilterVerificationInfo(ivi);
-            return false;
+            return true;
         }
-        return true;
+        return false;
     }
 
     int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
@@ -994,17 +997,43 @@
     }
 
     boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
-        PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
+        // Update the status for the current package
+        PackageSetting current = mPackages.get(packageName);
+        if (current == null) {
             Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
             return false;
         }
-        ps.setDomainVerificationStatusForUser(status, userId);
+        current.setDomainVerificationStatusForUser(status, userId);
+
+        if (current.getIntentFilterVerificationInfo() == null) {
+            Slog.w(PackageManagerService.TAG,
+                    "No IntentFilterVerificationInfo known for name: " + packageName);
+            return false;
+        }
+
+        // Then, if we set a ALWAYS status, then put NEVER status for Apps whose IntentFilter
+        // domains overlap the domains of the current package
+        ArraySet<String> currentDomains = current.getIntentFilterVerificationInfo().getDomainsSet();
+        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+            for (PackageSetting ps : mPackages.values()) {
+                if (ps == null || ps.pkg.packageName.equals(packageName)) continue;
+                IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+                if (ivi == null) {
+                    continue;
+                }
+                ArraySet<String> set = ivi.getDomainsSet();
+                set.retainAll(currentDomains);
+                if (set.size() > 0) {
+                    ps.setDomainVerificationStatusForUser(
+                            INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, userId);
+                }
+            }
+        }
         return true;
     }
 
     /**
-     * Used for dump. Should be read only.
+     * Used for Settings App and PackageManagerService dump. Should be read only.
      */
     List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
             String packageName) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index c6951bd..1a125d4 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -24,6 +24,7 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.Message;
@@ -63,6 +64,7 @@
     Display mDisplay;
 
     private final Region mTmpRegion = new Region();
+    private final Rect mTmpRect = new Rect();
 
     DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
             int flags, IBinder localWin) {
@@ -411,6 +413,12 @@
                 continue;
             }
 
+            child.getStackBounds(mTmpRect);
+            if (!mTmpRect.contains(x, y)) {
+                // outside of this window's activity stack == don't tell about drags
+                continue;
+            }
+
             child.getTouchableRegion(mTmpRegion);
 
             final int touchFlags = flags &
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index c8b6846..79510d0 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -759,6 +759,11 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public List<IntentFilter> getAllIntentFilters(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide
      */