Merge "Add a debug assert to track down infinite loop"
diff --git a/api/current.txt b/api/current.txt
index dd9b5a1..9df1023 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28,7 +28,6 @@
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
-    field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
@@ -34425,39 +34424,6 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
-    ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
-    method public final void clearAnnotation(java.lang.String);
-    method public void onNotificationActionClick(java.lang.String, long, int);
-    method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
-    method public void onNotificationRemoved(java.lang.String, long, int);
-    method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
-    method public final void setAnnotation(java.lang.String, android.app.Notification);
-    field public static final int REASON_APP_CANCEL = 8; // 0x8
-    field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
-    field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
-    field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
-    field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
-    field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
-    field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
-    field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
-    field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf
-    field public static final int REASON_PROFILE_TURNED_OFF = 16; // 0x10
-    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
-    field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
-  }
-
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
diff --git a/api/system-current.txt b/api/system-current.txt
index ef4841e..03167f6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -41,7 +41,6 @@
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
-    field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
@@ -36839,13 +36838,11 @@
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
     method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
-    method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
     method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
-    method public final void setAnnotation(java.lang.String, android.app.Notification);
     field public static final int REASON_APP_CANCEL = 8; // 0x8
     field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
     field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index 80d82c3..d6175af 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28,7 +28,6 @@
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
-    field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
     field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
@@ -34440,39 +34439,6 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
-    ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
-    method public final void clearAnnotation(java.lang.String);
-    method public void onNotificationActionClick(java.lang.String, long, int);
-    method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
-    method public void onNotificationRemoved(java.lang.String, long, int);
-    method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
-    method public final void setAnnotation(java.lang.String, android.app.Notification);
-    field public static final int REASON_APP_CANCEL = 8; // 0x8
-    field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
-    field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
-    field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
-    field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
-    field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
-    field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
-    field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
-    field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf
-    field public static final int REASON_PROFILE_TURNED_OFF = 16; // 0x10
-    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
-    field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
-  }
-
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 1ab55dd..980329f 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1030,6 +1030,20 @@
         }
     }
 
+    /**
+     * @hide
+     * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
+     * if defined (i.e. sequential or together), then we can use the flag instead of calculate
+     * dynamically.
+     * @return whether all the animators in the set are supposed to play together
+     */
+    public boolean shouldPlayTogether() {
+        updateAnimatorsDuration();
+        createDependencyGraph();
+        // All the child nodes are set out to play right after the delay animation
+        return mRootNode.mChildNodes.size() == mNodes.size() - 1;
+    }
+
     @Override
     public long getTotalDuration() {
         updateAnimatorsDuration();
diff --git a/core/java/android/animation/PathKeyframes.java b/core/java/android/animation/PathKeyframes.java
index 2a47b68..8230ac5 100644
--- a/core/java/android/animation/PathKeyframes.java
+++ b/core/java/android/animation/PathKeyframes.java
@@ -231,7 +231,7 @@
         }
     }
 
-    private abstract static class IntKeyframesBase extends SimpleKeyframes implements IntKeyframes {
+    abstract static class IntKeyframesBase extends SimpleKeyframes implements IntKeyframes {
         @Override
         public Class getType() {
             return Integer.class;
@@ -243,7 +243,7 @@
         }
     }
 
-    private abstract static class FloatKeyframesBase extends SimpleKeyframes
+    abstract static class FloatKeyframesBase extends SimpleKeyframes
             implements FloatKeyframes {
         @Override
         public Class getType() {
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index e993cca..6ba5b96 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -21,6 +21,7 @@
 import android.util.FloatProperty;
 import android.util.IntProperty;
 import android.util.Log;
+import android.util.PathParser;
 import android.util.Property;
 
 import java.lang.reflect.InvocationTargetException;
@@ -1046,6 +1047,43 @@
         return mAnimatedValue;
     }
 
+    /**
+     * PropertyValuesHolder is Animators use to hold internal animation related data.
+     * Therefore, in order to replicate the animation behavior, we need to get data out of
+     * PropertyValuesHolder.
+     * @hide
+     */
+    public void getPropertyValues(PropertyValues values) {
+        init();
+        values.propertyName = mPropertyName;
+        values.type = mValueType;
+        values.startValue = mKeyframes.getValue(0);
+        if (values.startValue instanceof PathParser.PathData) {
+            // PathData evaluator returns the same mutable PathData object when query fraction,
+            // so we have to make a copy here.
+            values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue);
+        }
+        values.endValue = mKeyframes.getValue(1);
+        if (values.endValue instanceof PathParser.PathData) {
+            // PathData evaluator returns the same mutable PathData object when query fraction,
+            // so we have to make a copy here.
+            values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue);
+        }
+        // TODO: We need a better way to get data out of keyframes.
+        if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
+                || mKeyframes instanceof PathKeyframes.IntKeyframesBase) {
+            // property values will animate based on external data source (e.g. Path)
+            values.dataSource = new PropertyValues.DataSource() {
+                @Override
+                public Object getValueAtFraction(float fraction) {
+                    return mKeyframes.getValue(fraction);
+                }
+            };
+        } else {
+            values.dataSource = null;
+        }
+    }
+
     @Override
     public String toString() {
         return mPropertyName + ": " + mKeyframes.toString();
@@ -1601,6 +1639,24 @@
         }
     };
 
+    /**
+     * @hide
+     */
+    public static class PropertyValues {
+        public String propertyName;
+        public Class type;
+        public Object startValue;
+        public Object endValue;
+        public DataSource dataSource = null;
+        public interface DataSource {
+            Object getValueAtFraction(float fraction);
+        }
+        public String toString() {
+            return ("property name: " + propertyName + ", type: " + type + ", startValue: "
+                    + startValue.toString() + ", endValue: " + endValue.toString());
+        }
+    }
+
     native static private long nGetIntMethod(Class targetClass, String methodName);
     native static private long nGetFloatMethod(Class targetClass, String methodName);
     native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 34c90c1..a78076b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3236,6 +3236,7 @@
                 return;
             }
             contentView.setTextViewText(R.id.app_name_text, appName);
+            contentView.setTextColor(R.id.app_name_text, resolveColor());
         }
 
         private void bindSmallIcon(RemoteViews contentView) {
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index a88aa3125..2937ccc 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -47,20 +47,6 @@
     public long mLastTimeUsed;
 
     /**
-     * The last time the package was used via implicit, non-user initiated actions (service
-     * was bound, etc).
-     * {@hide}
-     */
-    public long mLastTimeSystemUsed;
-
-    /**
-     * Last time the package was used and the beginning of the idle countdown.
-     * This uses a different timebase that is about how much the device has been in use in general.
-     * {@hide}
-     */
-    public long mBeginIdleTime;
-
-    /**
      * {@hide}
      */
     public long mTotalTimeInForeground;
@@ -89,8 +75,6 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
-        mBeginIdleTime = stats.mBeginIdleTime;
-        mLastTimeSystemUsed = stats.mLastTimeSystemUsed;
     }
 
     public String getPackageName() {
@@ -127,25 +111,6 @@
     }
 
     /**
-     * @hide
-     * Get the last time this package was used by the system (not the user). This can be different
-     * from {@link #getLastTimeUsed()} when the system binds to one of this package's services.
-     * See {@link System#currentTimeMillis()}.
-     */
-    public long getLastTimeSystemUsed() {
-        return mLastTimeSystemUsed;
-    }
-
-    /**
-     * @hide
-     * Get the last time this package was active, measured in milliseconds. This timestamp
-     * uses a timebase that represents how much the device was used and not wallclock time.
-     */
-    public long getBeginIdleTime() {
-        return mBeginIdleTime;
-    }
-
-    /**
      * Get the total time this package spent in the foreground, measured in milliseconds.
      */
     public long getTotalTimeInForeground() {
@@ -172,8 +137,6 @@
             // regards to their mEndTimeStamp.
             mLastEvent = right.mLastEvent;
             mLastTimeUsed = right.mLastTimeUsed;
-            mBeginIdleTime = right.mBeginIdleTime;
-            mLastTimeSystemUsed = right.mLastTimeSystemUsed;
         }
         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
@@ -195,8 +158,6 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
-        dest.writeLong(mBeginIdleTime);
-        dest.writeLong(mLastTimeSystemUsed);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -210,8 +171,6 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
-            stats.mBeginIdleTime = in.readLong();
-            stats.mLastTimeSystemUsed = in.readLong();
             return stats;
         }
 
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 4135487..9221fbb 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -148,8 +148,7 @@
                 return null;
             }
 
-            if ("com.google.android.gms".equals(mPackageName)
-                    || "com.google.android.syncadapters.contacts".equals(mPackageName)) {
+            if ("com.google.android.gms".equals(mPackageName)) {
                 // They're casting to a concrete subclass, sigh
                 return cursor;
             } else {
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index b3e3ab6..4487121 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -98,8 +98,13 @@
     /**
      * Send raw NFC-F commands to the tag and receive the response.
      *
-     * <p>Applications must not append the SoD (length) or EoD (CRC) to the payload,
-     * it will be automatically calculated.
+     * <p>Applications must not prefix the SoD (preamble and sync code)
+     * and/or append the EoD (CRC) to the payload, it will be automatically calculated.
+     *
+     * <p>A typical NFC-F frame for this method looks like:
+     * <pre>
+     * LENGTH (1 byte) --- CMD (1 byte) -- IDm (8 bytes) -- PARAMS (LENGTH - 10 bytes)
+     * </pre>
      *
      * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes
      * that can be sent with {@link #transceive}.
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 344d06e..24666fe 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -74,6 +74,9 @@
     /** @hide A user id constant to indicate the "system" user of the device */
     public static final @UserIdInt int USER_SYSTEM = 0;
 
+    /** @hide A user serial constant to indicate the "system" user of the device */
+    public static final int USER_SERIAL_SYSTEM = 0;
+
     /** @hide A user handle to indicate the "system" user of the device */
     public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
 
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 9b3f02d..dd8eb5f 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1284,7 +1284,7 @@
 
             @Override
             public void prepareUserStorage(
-                    String volumeUuid, int userId, int serialNumber, boolean ephemeral)
+                    String volumeUuid, int userId, int serialNumber, int flags)
                     throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1293,7 +1293,7 @@
                     _data.writeString(volumeUuid);
                     _data.writeInt(userId);
                     _data.writeInt(serialNumber);
-                    _data.writeInt(ephemeral ? 1 : 0);
+                    _data.writeInt(flags);
                     mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -2055,8 +2055,8 @@
                     String volumeUuid = data.readString();
                     int userId = data.readInt();
                     int serialNumber = data.readInt();
-                    boolean ephemeral = data.readInt() != 0;
-                    prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
+                    int _flags = data.readInt();
+                    prepareUserStorage(volumeUuid, userId, serialNumber, _flags);
                     reply.writeNoException();
                     return true;
                 }
@@ -2389,7 +2389,7 @@
     public boolean isUserKeyUnlocked(int userId) throws RemoteException;
 
     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
-            boolean ephemeral) throws RemoteException;
+            int flags) throws RemoteException;
 
     public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index c8b942b..b82638a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -92,8 +92,14 @@
     /** {@hide} */
     public static final int DEBUG_EMULATE_FBE = 1 << 1;
 
+    // NOTE: keep in sync with installd
     /** {@hide} */
-    public static final int FLAG_FOR_WRITE = 1 << 0;
+    public static final int FLAG_STORAGE_DE = 1 << 0;
+    /** {@hide} */
+    public static final int FLAG_STORAGE_CE = 1 << 1;
+
+    /** {@hide} */
+    public static final int FLAG_FOR_WRITE = 1 << 8;
 
     private final Context mContext;
     private final ContentResolver mResolver;
@@ -1003,10 +1009,9 @@
     }
 
     /** {@hide} */
-    public void prepareUserStorage(
-            String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
         try {
-            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
+            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 7a9d062..ea54f92 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -20,35 +20,126 @@
 import android.os.Bundle;
 
 /**
- * Constants and methods to access blocked phone numbers for incoming calls and texts.
+ * <p>
+ * The contract between the blockednumber provider and applications. Contains definitions for
+ * the supported URIs and columns.
+ * </p>
  *
- * TODO javadoc
- * - Proper javadoc tagging.
- * - Code sample?
- * - Describe who can access it.
+ * <h3> Overview </h3>
+ * <p>
+ * The content provider exposes a table containing blocked numbers. The columns and URIs for
+ * accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from
+ * blocked numbers are discarded by the platform. Notifications upon provider changes can be
+ * received using a {@link android.database.ContentObserver}.
+ * </p>
+ *
+ * <h3> Permissions </h3>
+ * <p>
+ * Only the system, the default SMS application, and the default phone app
+ * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps
+ * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber
+ * provider.
+ * </p>
+ *
+ * <h3> Data </h3>
+ * <p>
+ * Other than regular phone numbers, the blocked number provider can also store addresses (such
+ * as email) from which a user can receive messages, and calls. The blocked numbers are stored
+ * in the {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column. A normalized version of phone
+ * numbers (if normalization is possible) is stored in {@link BlockedNumbers#COLUMN_E164_NUMBER}
+ * column. The platform blocks calls, and messages from an address if it is present in in the
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or if the E164 version of the address
+ * matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+ * </p>
+ *
+ * <h3> Operations </h3>
+ * <dl>
+ * <dt><b>Insert</b></dt>
+ * <dd>
+ * <p>
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} is a required column that needs to be populated.
+ * Apps can optionally provide the {@link BlockedNumbers#COLUMN_E164_NUMBER} which is the phone
+ * number's E164 representation. The provider automatically populates this column if the app does
+ * not provide it. Note that this column is not populated if normalization fails or if the address
+ * is not a phone number (eg: email). The provider enforces uniqueness constraint on this column.
+ * Examples:
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * </pre>
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
+ * values.put(BlockedNumbers.COLUMN_E164_NUMBER, "+11234567890");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * </pre>
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "12345@abdcde.com");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * </pre>
+ * </p>
+ * </dd>
+ * <dt><b>Update</b></dt>
+ * <dd>
+ * <p>
+ * Updates are not supported. Use Delete, and Insert instead.
+ * </p>
+ * </dd>
+ * <dt><b>Delete</b></dt>
+ * <dd>
+ * <p>
+ * Deletions can be performed as follows:
+ * <pre>
+ * ContentValues values = new ContentValues();
+ * values.put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, "1234567890");
+ * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
+ * getContentResolver().delete(uri, null, null);
+ * </pre>
+ * </p>
+ * </dd>
+ * <dt><b>Query</b></dt>
+ * <dd>
+ * <p>
+ * All blocked numbers can be enumerated as follows:
+ * <pre>
+ * Cursor c = getContentResolver().query(BlockedNumbers.CONTENT_URI,
+ *          new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
+ *          BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
+ * </pre>
+ * To check if a particular number is blocked, use the method
+ * {@link #isBlocked(Context, String)}.
+ * </p>
+ * </dd>
+ *
+ * <h3> Multi-user </h3>
+ * <p>
+ * Apps must use the method {@link #canCurrentUserBlockNumbers(Context)} before performing any
+ * operation on the blocked number provider. If {@link #canCurrentUserBlockNumbers(Context)} returns
+ * {@code false}, all operations on the provider will fail with an
+ * {@link UnsupportedOperationException}. The platform will block calls, and messages from numbers
+ * in the provider independent of the current user.
+ * </p>
  */
 public class BlockedNumberContract {
     private BlockedNumberContract() {
     }
 
-    /** The authority for the contacts provider */
+    /** The authority for the blocked number provider */
     public static final String AUTHORITY = "com.android.blockednumber";
 
-    /** A content:// style uri to the authority for the contacts provider */
+    /** A content:// style uri to the authority for the blocked number provider */
     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
 
     /**
-     * TODO javadoc
-     *
-     * Constants to interact with the blocked phone number list.
+     * Constants to interact with the blocked numbers list.
      */
     public static class BlockedNumbers {
         private BlockedNumbers() {
         }
 
         /**
-         * TODO javadoc
-         *
          * Content URI for the blocked numbers.
          *
          * Supported operations
@@ -117,8 +208,8 @@
      * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
      */
     public static boolean isBlocked(Context context, String phoneNumber) {
-        final Bundle res = context.getContentResolver().call(AUTHORITY_URI,
-                METHOD_IS_BLOCKED, phoneNumber, null);
+        final Bundle res = context.getContentResolver().call(
+                AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
         return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
     }
 
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 330fcf6..dfdd36d 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2079,10 +2079,11 @@
             if (preferHighres) {
                 final Uri displayPhotoUri = Uri.withAppendedPath(contactUri,
                         Contacts.Photo.DISPLAY_PHOTO);
-                InputStream inputStream;
                 try {
                     AssetFileDescriptor fd = cr.openAssetFileDescriptor(displayPhotoUri, "r");
-                    return fd.createInputStream();
+                    if (fd != null) {
+                        return fd.createInputStream();
+                    }
                 } catch (IOException e) {
                     // fallback to the thumbnail code
                 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index a56e030..fb58f4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -45,7 +45,9 @@
  *         &lt;action android:name="android.service.notification.NotificationAssistantService" />
  *     &lt;/intent-filter>
  * &lt;/service></pre>
+ * @hide
  */
+@SystemApi
 public abstract class NotificationAssistantService extends NotificationListenerService {
     private static final String TAG = "NotificationAssistant";
 
@@ -210,29 +212,6 @@
         }
     }
 
-    /**
-     * Add an annotation to a an existing notification. The delete intent will
-     * be fired when the host notification is deleted, or when this annotation
-     * is removed or replaced.
-     *
-     * @param key the key of the notification to be annotated
-     * @param annotation the new annotation object
-     */
-    public final void setAnnotation(String key, Notification annotation)
-    {
-        // TODO: pack up the annotation and send it to the NotificationManager.
-    }
-
-    /**
-     * Remove the annotation from a notification.
-     *
-     * @param key the key of the notification to be cleansed of annotatons
-     */
-    public final void clearAnnotation(String key)
-    {
-        // TODO: ask the NotificationManager to clear the annotation.
-    }
-
     private class NotificationAssistantWrapper extends NotificationListenerWrapper {
         @Override
         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index ed90e79..7ff883e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -695,7 +695,7 @@
     /**
      * Request that the listener be rebound, after a previous call to (@link requestUnbind).
      *
-     * <P>This method will fail for assistants that have
+     * <P>This method will fail for listeners that have
      * not been granted the permission by the user.
      *
      * <P>The service should wait for the {@link #onListenerConnected()} event
@@ -1022,8 +1022,7 @@
         }
 
         /**
-         * If the importance has been overriden by user preference, or by a
-         * {@link NotificationAssistantService}, then this will be non-null,
+         * If the importance has been overriden by user preference, then this will be non-null,
          * and should be displayed to the user.
          *
          * @return the explanation for the importance, or null if it is the natural importance
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 78d5bcd..29a72fd 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -104,6 +104,7 @@
             }
             super.finalize();
         }
+
     }
 
     /**
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 3122c0b..7017ff5 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -22,6 +22,7 @@
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
 
 /**
  * <p>A display list records a series of graphics related operations and can replay
@@ -774,6 +775,14 @@
         mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
     }
 
+    public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimator animatorSet) {
+        if (mOwningView == null || mOwningView.mAttachInfo == null) {
+            throw new IllegalStateException("Cannot start this animator on a detached view!");
+        }
+        nAddAnimator(mNativeRenderNode, animatorSet.getAnimatorNativePtr());
+        mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
+    }
+
     public void endAllAnimators() {
         nEndAllAnimators(mNativeRenderNode);
     }
diff --git a/core/java/android/view/RenderNodeAnimatorSetHelper.java b/core/java/android/view/RenderNodeAnimatorSetHelper.java
new file mode 100644
index 0000000..ba592d29
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimatorSetHelper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.animation.TimeInterpolator;
+import com.android.internal.view.animation.FallbackLUTInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
+/**
+ * This is a helper class to get access to methods and fields needed for RenderNodeAnimatorSet
+ * that are internal or package private to android.view package.
+ *
+ * @hide
+ */
+public class RenderNodeAnimatorSetHelper {
+
+    public static RenderNode getTarget(DisplayListCanvas recordingCanvas) {
+        return recordingCanvas.mNode;
+    }
+
+    public static long createNativeInterpolator(TimeInterpolator interpolator, long
+            duration) {
+        if (interpolator == null) {
+            // create LinearInterpolator
+            return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+        } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+            return ((NativeInterpolatorFactory)interpolator).createNativeInterpolator();
+        } else {
+            return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
+        }
+    }
+
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ffa3fa6..1b6b53a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -51,6 +51,7 @@
     android_database_SQLiteConnection.cpp \
     android_database_SQLiteGlobal.cpp \
     android_database_SQLiteDebug.cpp \
+    android_graphics_drawable_AnimatedVectorDrawable.cpp \
     android_graphics_drawable_VectorDrawable.cpp \
     android_view_DisplayEventReceiver.cpp \
     android_view_DisplayListCanvas.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2e45f8c..223fc1a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -131,6 +131,7 @@
 extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
 extern int register_android_graphics_Xfermode(JNIEnv* env);
+extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
@@ -1321,6 +1322,7 @@
     REG_JNI(register_android_graphics_Typeface),
     REG_JNI(register_android_graphics_Xfermode),
     REG_JNI(register_android_graphics_YuvImage),
+    REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
     REG_JNI(register_android_graphics_pdf_PdfDocument),
     REG_JNI(register_android_graphics_pdf_PdfEditor),
diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
new file mode 100644
index 0000000..7a3c598
--- /dev/null
+++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+#include "log/log.h"
+
+#include "Animator.h"
+#include "Interpolator.h"
+#include "PropertyValuesAnimatorSet.h"
+#include "PropertyValuesHolder.h"
+#include "VectorDrawable.h"
+
+namespace android {
+using namespace uirenderer;
+using namespace VectorDrawable;
+
+static struct {
+    jclass clazz;
+    jmethodID callOnFinished;
+} gVectorDrawableAnimatorClassInfo;
+
+static JNIEnv* getEnv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return 0;
+    }
+    return env;
+}
+
+static AnimationListener* createAnimationListener(JNIEnv* env, jobject finishListener) {
+    class AnimationListenerBridge : public AnimationListener {
+    public:
+        AnimationListenerBridge(JNIEnv* env, jobject finishListener) {
+            mFinishListener = env->NewGlobalRef(finishListener);
+            env->GetJavaVM(&mJvm);
+        }
+
+        virtual ~AnimationListenerBridge() {
+            if (mFinishListener) {
+                onAnimationFinished(NULL);
+            }
+        }
+
+        virtual void onAnimationFinished(BaseRenderNodeAnimator*) {
+            LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?");
+            JNIEnv* env = getEnv(mJvm);
+            env->CallStaticVoidMethod(
+                    gVectorDrawableAnimatorClassInfo.clazz,
+                    gVectorDrawableAnimatorClassInfo.callOnFinished,
+                    mFinishListener);
+            releaseJavaObject();
+        }
+
+    private:
+        void releaseJavaObject() {
+            JNIEnv* env = getEnv(mJvm);
+            env->DeleteGlobalRef(mFinishListener);
+            mFinishListener = NULL;
+        }
+
+        JavaVM* mJvm;
+        jobject mFinishListener;
+    };
+    return new AnimationListenerBridge(env, finishListener);
+}
+
+static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr,
+        jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
+    Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
+    set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount);
+}
+
+static jlong createAnimatorSet(JNIEnv*, jobject) {
+    PropertyValuesAnimatorSet* animatorSet = new PropertyValuesAnimatorSet();
+    return reinterpret_cast<jlong>(animatorSet);
+}
+
+static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+        jfloat startValue, jfloat endValue) {
+    VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(nativePtr);
+    GroupPropertyValuesHolder* newHolder = new GroupPropertyValuesHolder(group, propertyId,
+            startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathDataPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jlong startValuePtr,
+        jlong endValuePtr) {
+    VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(nativePtr);
+    PathData* startData = reinterpret_cast<PathData*>(startValuePtr);
+    PathData* endData = reinterpret_cast<PathData*>(endValuePtr);
+    PathDataPropertyValuesHolder* newHolder = new PathDataPropertyValuesHolder(path,
+            startData, endData);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathColorPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+        int startValue, jint endValue) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr);
+    FullPathColorPropertyValuesHolder* newHolder = new FullPathColorPropertyValuesHolder(fullPath,
+            propertyId, startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+        float startValue, jfloat endValue) {
+    VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr);
+    FullPathPropertyValuesHolder* newHolder = new FullPathPropertyValuesHolder(fullPath,
+            propertyId, startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jfloat startValue,
+        float endValue) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(nativePtr);
+    RootAlphaPropertyValuesHolder* newHolder = new RootAlphaPropertyValuesHolder(tree,
+            startValue, endValue);
+    return reinterpret_cast<jlong>(newHolder);
+}
+static void setPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
+        jfloatArray srcData, jint length) {
+
+    jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr);
+    PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
+    holder->setPropertyDataSource(propertyData, length);
+    env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT);
+}
+static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    // TODO: keep a ref count in finish listener
+    AnimationListener* listener = createAnimationListener(env, finishListener);
+    set->start(listener);
+}
+
+static void reverse(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener) {
+    // TODO: implement reverse
+}
+
+static void end(JNIEnv*, jobject, jlong animatorSetPtr) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    set->end();
+}
+
+static void reset(JNIEnv*, jobject, jlong animatorSetPtr) {
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+    set->reset();
+}
+
+static const JNINativeMethod gMethods[] = {
+    {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet},
+    {"nAddAnimator", "(JJJJJI)V", (void*)addAnimator},
+    {"nCreateGroupPropertyHolder", "!(JIFF)J", (void*)createGroupPropertyHolder},
+    {"nCreatePathDataPropertyHolder", "!(JJJ)J", (void*)createPathDataPropertyHolder},
+    {"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder},
+    {"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder},
+    {"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder},
+    {"nSetPropertyHolderData", "(J[FI)V", (void*)setPropertyHolderData},
+    {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;)V", (void*)start},
+    {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;)V", (void*)reverse},
+    {"nEnd", "!(J)V", (void*)end},
+    {"nReset", "!(J)V", (void*)reset},
+};
+
+const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator";
+int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env) {
+    gVectorDrawableAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
+    gVectorDrawableAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
+            gVectorDrawableAnimatorClassInfo.clazz);
+
+    gVectorDrawableAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
+            env, gVectorDrawableAnimatorClassInfo.clazz, "callOnFinished",
+            "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;)V");
+    return RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedVectorDrawable",
+            gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index 563ec8b..7314fbc 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -32,11 +32,6 @@
     return reinterpret_cast<jlong>(tree);
 }
 
-static void deleteTree(JNIEnv*, jobject, jlong treePtr) {
-    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
-    delete tree;
-}
-
 static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr,
         jfloat viewportWidth, jfloat viewportHeight) {
     VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
@@ -143,11 +138,6 @@
     return reinterpret_cast<jlong>(newGroup);
 }
 
-static void deleteNode(JNIEnv*, jobject, jlong nodePtr) {
-    VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
-    delete node;
-}
-
 static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) {
     VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
     const char* nodeName = env->GetStringUTFChars(nameStr, NULL);
@@ -333,7 +323,6 @@
 
 static const JNINativeMethod gMethods[] = {
         {"nCreateRenderer", "!(J)J", (void*)createTree},
-        {"nDestroyRenderer", "!(J)V", (void*)deleteTree},
         {"nSetRendererViewportSize", "!(JFF)V", (void*)setTreeViewportSize},
         {"nSetRootAlpha", "!(JF)Z", (void*)setRootAlpha},
         {"nGetRootAlpha", "!(J)F", (void*)getRootAlpha},
@@ -352,7 +341,6 @@
         {"nCreateClipPath", "!(J)J", (void*)createClipPath},
         {"nCreateGroup", "!()J", (void*)createEmptyGroup},
         {"nCreateGroup", "!(J)J", (void*)createGroup},
-        {"nDestroy", "!(J)V", (void*)deleteNode},
         {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName},
         {"nUpdateGroupProperties", "!(JFFFFFFF)V", (void*)updateGroupProperties},
 
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 4842e1b..e39bb1c 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -205,8 +205,8 @@
     SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
 
     Sensor const* const* sensorList;
-    size_t count = mgr->getSensorList(&sensorList);
-    if (size_t(index) >= count) {
+    ssize_t count = mgr->getSensorList(&sensorList);
+    if (ssize_t(index) >= count) {
         return false;
     }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cc48902..4cddb6c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2739,6 +2739,7 @@
          android.service.notification.NotificationAssistantService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature
+         @hide This is not a third-party API (intended for system apps). -->
     -->
     <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
         android:protectionLevel="signature" />
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index dd18544..7399fa9 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -48,8 +48,6 @@
     <color name="primary_text_default_material_light">#de000000</color>
     <!-- 54% black -->
     <color name="secondary_text_default_material_light">#8a000000</color>
-    <!-- 38% black -->
-    <color name="tertiary_text_default_material_light">#61000000</color>
 
     <!-- 100% white -->
     <color name="primary_text_default_material_dark">#ffffffff</color>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 0a52f41..9efcfda 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -444,7 +444,7 @@
     </style>
 
     <style name="TextAppearance.Material.Notification.Info">
-        <item name="textColor">@color/tertiary_text_default_material_light</item>
+        <item name="textColor">@color/secondary_text_default_material_light</item>
         <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 961d0eb..dc302c7 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -344,6 +344,9 @@
     <family lang="und-Zsye">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
+    <family>
+        <font weight="400" style="normal">DroidSansFallback.ttf</font>
+    </family>
     <!--
         Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index c48c371..af8ccf5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,8 +19,14 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.Animator.AnimatorListener;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.app.Application;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -31,17 +37,27 @@
 import android.graphics.Outline;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.os.Build;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.LongArray;
+import android.util.PathParser;
+import android.util.TimeUtils;
+import android.view.Choreographer;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.RenderNodeAnimatorSetHelper;
 import android.view.View;
 
 import com.android.internal.R;
 
+import com.android.internal.util.VirtualRefBasePtr;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
@@ -138,7 +154,7 @@
     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
 
     /** Local, mutable animator set. */
-    private final AnimatorSet mAnimatorSet = new AnimatorSet();
+    private final VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimator();
 
     /**
      * The resources against which this drawable was created. Used to attempt
@@ -187,6 +203,24 @@
         mMutated = false;
     }
 
+    /**
+     * In order to avoid breaking old apps, we only throw exception on invalid VectorDrawable
+     * animations * for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
+     * these animations.
+     *
+     * @return whether invalid animations for vector drawable should be ignored.
+     */
+    private static boolean shouldIgnoreInvalidAnimation() {
+        Application app = ActivityThread.currentApplication();
+        if (app == null || app.getApplicationInfo() == null) {
+            return true;
+        }
+        if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public ConstantState getConstantState() {
         mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
@@ -200,6 +234,9 @@
 
     @Override
     public void draw(Canvas canvas) {
+        if (canvas.isHardwareAccelerated()) {
+            mAnimatorSet.recordLastSeenTarget((DisplayListCanvas) canvas);
+        }
         mAnimatedVectorState.mVectorDrawable.draw(canvas);
         if (isStarted()) {
             invalidateSelf();
@@ -582,9 +619,8 @@
      * Resets the AnimatedVectorDrawable to the start state as specified in the animators.
      */
     public void reset() {
-        // TODO: Use reverse or seek to implement reset, when AnimatorSet supports them.
-        start();
-        mAnimatorSet.cancel();
+        mAnimatorSet.reset();
+        invalidateSelf();
     }
 
     @Override
@@ -603,8 +639,12 @@
     @NonNull
     private void ensureAnimatorSet() {
         if (!mHasAnimatorSet) {
-            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
+            // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
+            // with a list of LocalAnimators.
+            AnimatorSet set = new AnimatorSet();
+            mAnimatedVectorState.prepareLocalAnimators(set, mRes);
             mHasAnimatorSet = true;
+            mAnimatorSet.initWithAnimatorSet(set);
             mRes = null;
         }
     }
@@ -694,13 +734,13 @@
                 }
             };
         }
-        mAnimatorSet.addListener(mAnimatorListener);
+        mAnimatorSet.setListener(mAnimatorListener);
     }
 
     // A helper function to clean up the animator listener in the mAnimatorSet.
     private void removeAnimatorSetListener() {
         if (mAnimatorListener != null) {
-            mAnimatorSet.removeListener(mAnimatorListener);
+            mAnimatorSet.removeListener();
             mAnimatorListener = null;
         }
     }
@@ -729,4 +769,427 @@
 
         mAnimationCallbacks.clear();
     }
+
+    /**
+     * @hide
+     */
+    public static class VectorDrawableAnimator {
+        private AnimatorListener mListener = null;
+        private final LongArray mStartDelays = new LongArray();
+        private PropertyValuesHolder.PropertyValues mTmpValues =
+                new PropertyValuesHolder.PropertyValues();
+        private long mSetPtr = 0;
+        private boolean mContainsSequentialAnimators = false;
+        private boolean mStarted = false;
+        private boolean mInitialized = false;
+        private boolean mAnimationPending = false;
+        private boolean mIsReversible = false;
+        // This needs to be set before parsing starts.
+        private boolean mShouldIgnoreInvalidAnim;
+        // TODO: Consider using NativeAllocationRegistery to track native allocation
+        private final VirtualRefBasePtr mSetRefBasePtr;
+        private WeakReference<RenderNode> mTarget = null;
+        private WeakReference<RenderNode> mLastSeenTarget = null;
+
+
+        VectorDrawableAnimator() {
+            mSetPtr = nCreateAnimatorSet();
+            // Increment ref count on native AnimatorSet, so it doesn't get released before Java
+            // side is done using it.
+            mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
+        }
+
+        private void initWithAnimatorSet(AnimatorSet set) {
+            if (mInitialized) {
+                // Already initialized
+                throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
+                        "re-initialized");
+            }
+            mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
+            parseAnimatorSet(set, 0);
+            mInitialized = true;
+
+            // Check reversible.
+            if (mContainsSequentialAnimators) {
+                mIsReversible = false;
+            } else {
+                // Check if there's any start delay set on child
+                for (int i = 0; i < mStartDelays.size(); i++) {
+                    if (mStartDelays.get(i) > 0) {
+                        mIsReversible = false;
+                        return;
+                    }
+                }
+            }
+            mIsReversible = true;
+        }
+
+        private void parseAnimatorSet(AnimatorSet set, long startTime) {
+            ArrayList<Animator> animators = set.getChildAnimations();
+
+            boolean playTogether = set.shouldPlayTogether();
+            // Convert AnimatorSet to VectorDrawableAnimator
+            for (int i = 0; i < animators.size(); i++) {
+                Animator animator = animators.get(i);
+                // Here we only support ObjectAnimator
+                if (animator instanceof AnimatorSet) {
+                    parseAnimatorSet((AnimatorSet) animator, startTime);
+                } else if (animator instanceof ObjectAnimator) {
+                    createRTAnimator((ObjectAnimator) animator, startTime);
+                } // ignore ValueAnimators and others because they don't directly modify VD
+                  // therefore will be useless to AVD.
+
+                if (!playTogether) {
+                    // Assume not play together means play sequentially
+                    startTime += animator.getTotalDuration();
+                    mContainsSequentialAnimators = true;
+                }
+            }
+        }
+
+        // TODO: This method reads animation data from already parsed Animators. We need to move
+        // this step further up the chain in the parser to avoid the detour.
+        private void createRTAnimator(ObjectAnimator animator, long startTime) {
+            PropertyValuesHolder[] values = animator.getValues();
+            Object target = animator.getTarget();
+            if (target instanceof VectorDrawable.VGroup) {
+                createRTAnimatorForGroup(values, animator, (VectorDrawable.VGroup) target,
+                        startTime);
+            } else if (target instanceof VectorDrawable.VPath) {
+                for (int i = 0; i < values.length; i++) {
+                    values[i].getPropertyValues(mTmpValues);
+                    if (mTmpValues.endValue instanceof PathParser.PathData &&
+                            mTmpValues.propertyName.equals("pathData")) {
+                        createRTAnimatorForPath(animator, (VectorDrawable.VPath) target,
+                                startTime);
+                    }  else if (target instanceof VectorDrawable.VFullPath) {
+                        createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
+                                startTime);
+                    } else if (!mShouldIgnoreInvalidAnim) {
+                        throw new IllegalArgumentException("ClipPath only supports PathData " +
+                                "property");
+                    }
+
+                }
+            } else if (target instanceof VectorDrawable.VectorDrawableState) {
+                createRTAnimatorForRootGroup(values, animator,
+                        (VectorDrawable.VectorDrawableState) target, startTime);
+            } else if (!mShouldIgnoreInvalidAnim) {
+                // Should never get here
+                throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
+                        "or ConstantState, " + target == null ? "Null target" : target.getClass() +
+                        " is not supported");
+            }
+        }
+
+        private void createRTAnimatorForGroup(PropertyValuesHolder[] values,
+                ObjectAnimator animator, VectorDrawable.VGroup target,
+                long startTime) {
+
+            long nativePtr = target.getNativePtr();
+            int propertyId;
+            for (int i = 0; i < values.length; i++) {
+                // TODO: We need to support the rare case in AVD where no start value is provided
+                values[i].getPropertyValues(mTmpValues);
+                propertyId = VectorDrawable.VGroup.getPropertyIndex(mTmpValues.propertyName);
+                if (mTmpValues.type != Float.class && mTmpValues.type != float.class) {
+                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+                        Log.e(LOGTAG, "Unsupported type: " +
+                                mTmpValues.type + ". Only float value is supported for Groups.");
+                    }
+                    continue;
+                }
+                if (propertyId < 0) {
+                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+                        Log.e(LOGTAG, "Unsupported property: " +
+                                mTmpValues.propertyName + " for Vector Drawable Group");
+                    }
+                    continue;
+                }
+                long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
+                        (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
+                if (mTmpValues.dataSource != null) {
+                    float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
+                            .getDuration());
+                    nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+                }
+                createNativeChildAnimator(propertyPtr, startTime, animator);
+            }
+        }
+        private void createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target,
+                long startTime) {
+
+            long nativePtr = target.getNativePtr();
+            long startPathDataPtr = ((PathParser.PathData) mTmpValues.startValue)
+                    .getNativePtr();
+            long endPathDataPtr = ((PathParser.PathData) mTmpValues.endValue)
+                    .getNativePtr();
+            long propertyPtr = nCreatePathDataPropertyHolder(nativePtr, startPathDataPtr,
+                    endPathDataPtr);
+            createNativeChildAnimator(propertyPtr, startTime, animator);
+        }
+
+        private void createRTAnimatorForFullPath(ObjectAnimator animator,
+                VectorDrawable.VFullPath target, long startTime) {
+
+            int propertyId = target.getPropertyIndex(mTmpValues.propertyName);
+            long propertyPtr;
+            long nativePtr = target.getNativePtr();
+            if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
+                if (propertyId < 0) {
+                    if (mShouldIgnoreInvalidAnim) {
+                        return;
+                    } else {
+                        throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
+                                + " is not supported for FullPath");
+                    }
+                }
+                propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
+                        (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
+
+            } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
+                propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
+                        (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
+            } else {
+                if (mShouldIgnoreInvalidAnim) {
+                    return;
+                } else {
+                    throw new UnsupportedOperationException("Unsupported type: " +
+                            mTmpValues.type + ". Only float, int or PathData value is " +
+                            "supported for Paths.");
+                }
+            }
+            if (mTmpValues.dataSource != null) {
+                float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
+                        .getDuration());
+                nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+            }
+            createNativeChildAnimator(propertyPtr, startTime, animator);
+        }
+
+        private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
+                ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
+                long startTime) {
+                long nativePtr = target.getNativeRenderer();
+                if (!animator.getPropertyName().equals("alpha")) {
+                    if (mShouldIgnoreInvalidAnim) {
+                        return;
+                    } else {
+                        throw new UnsupportedOperationException("Only alpha is supported for root "
+                                + "group");
+                    }
+                }
+                Float startValue = null;
+                Float endValue = null;
+                for (int i = 0; i < values.length; i++) {
+                    values[i].getPropertyValues(mTmpValues);
+                    if (mTmpValues.propertyName.equals("alpha")) {
+                        startValue = (Float) mTmpValues.startValue;
+                        endValue = (Float) mTmpValues.endValue;
+                        break;
+                    }
+                }
+                if (startValue == null && endValue == null) {
+                    if (mShouldIgnoreInvalidAnim) {
+                        return;
+                    } else {
+                        throw new UnsupportedOperationException("No alpha values are specified");
+                    }
+                }
+                long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
+                createNativeChildAnimator(propertyPtr, startTime, animator);
+        }
+
+        // These are the data points that define the value of the animating properties.
+        // e.g. translateX and translateY can animate along a Path, at any fraction in [0, 1]
+        // a point on the path corresponds to the values of translateX and translateY.
+        // TODO: (Optimization) We should pass the path down in native and chop it into segments
+        // in native.
+        private static float[] createDataPoints(
+                PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
+            long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+            int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+            int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
+            float values[] = new float[numAnimFrames];
+            float lastFrame = numAnimFrames - 1;
+            for (int i = 0; i < numAnimFrames; i++) {
+                float fraction = i / lastFrame;
+                values[i] = (Float) dataSource.getValueAtFraction(fraction);
+            }
+            return values;
+        }
+
+        private void createNativeChildAnimator(long propertyPtr, long extraDelay,
+                                               ObjectAnimator animator) {
+            long duration = animator.getDuration();
+            int repeatCount = animator.getRepeatCount();
+            long startDelay = extraDelay + animator.getStartDelay();
+            TimeInterpolator interpolator = animator.getInterpolator();
+            long nativeInterpolator =
+                    RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
+
+            startDelay *= ValueAnimator.getDurationScale();
+            duration *= ValueAnimator.getDurationScale();
+
+            mStartDelays.add(startDelay);
+            nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
+                    repeatCount);
+        }
+
+        /**
+         * Holds a weak reference to the target that was last seen (through the DisplayListCanvas
+         * in the last draw call), so that when animator set needs to start, we can add the animator
+         * to the last seen RenderNode target and start right away.
+         */
+        protected void recordLastSeenTarget(DisplayListCanvas canvas) {
+            if (mAnimationPending) {
+                mLastSeenTarget = new WeakReference<RenderNode>(
+                        RenderNodeAnimatorSetHelper.getTarget(canvas));
+                if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+                    Log.d(LOGTAG, "Target is set in the next frame");
+                }
+                mAnimationPending = false;
+                start();
+            } else {
+                mLastSeenTarget = new WeakReference<RenderNode>(
+                        RenderNodeAnimatorSetHelper.getTarget(canvas));
+            }
+
+        }
+
+        private boolean setTarget(RenderNode node) {
+            if (mTarget != null && mTarget.get() != null) {
+                // TODO: Maybe we want to support target change.
+                throw new IllegalStateException("Target already set!");
+            }
+
+            node.addAnimator(this);
+            mTarget = new WeakReference<RenderNode>(node);
+            return true;
+        }
+
+        private boolean useLastSeenTarget() {
+            if (mLastSeenTarget != null && mLastSeenTarget.get() != null) {
+                setTarget(mLastSeenTarget.get());
+                return true;
+            }
+            return false;
+        }
+
+        public void start() {
+            if (!mInitialized) {
+                return;
+            }
+
+            if (mStarted) {
+                return;
+            }
+
+            if (!useLastSeenTarget()) {
+                mAnimationPending = true;
+                return;
+            }
+
+            if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+                Log.d(LOGTAG, "Target is set. Starting VDAnimatorSet from java");
+            }
+
+           nStart(mSetPtr, this);
+            if (mListener != null) {
+                mListener.onAnimationStart(null);
+            }
+            mStarted = true;
+        }
+
+        public void end() {
+            if (mInitialized && mStarted) {
+                nEnd(mSetPtr);
+                onAnimationEnd();
+            }
+        }
+
+        void reset() {
+            if (!mInitialized) {
+                return;
+            }
+            // TODO: Need to implement reset.
+            Log.w(LOGTAG, "Reset is yet to be implemented");
+            nReset(mSetPtr);
+        }
+
+        // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
+        // animators or when the animator set has a start delay
+        void reverse() {
+            if (!mIsReversible) {
+                return;
+            }
+            // TODO: Need to support reverse (non-public API)
+            Log.w(LOGTAG, "Reverse is yet to be implemented");
+            nReverse(mSetPtr, this);
+        }
+
+        public long getAnimatorNativePtr() {
+            return mSetPtr;
+        }
+
+        boolean canReverse() {
+            return mIsReversible;
+        }
+
+        boolean isStarted() {
+            return mStarted;
+        }
+
+        boolean isRunning() {
+            if (!mInitialized) {
+                return false;
+            }
+            return mStarted;
+        }
+
+        void setListener(AnimatorListener listener) {
+            mListener = listener;
+        }
+
+        void removeListener() {
+            mListener = null;
+        }
+
+        private void onAnimationEnd() {
+            mStarted = false;
+            if (mListener != null) {
+                mListener.onAnimationEnd(null);
+            }
+            mTarget = null;
+        }
+
+        // onFinished: should be called from native
+        private static void callOnFinished(VectorDrawableAnimator set) {
+            if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+                Log.d(LOGTAG, "on finished called from native");
+            }
+            set.onAnimationEnd();
+        }
+    }
+
+    private static native long nCreateAnimatorSet();
+    private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
+             long nativeInterpolator, long startDelay, long duration, int repeatCount);
+
+    private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
+            float startValue, float endValue);
+
+    private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
+            long endValuePtr);
+    private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
+            int startValue, int endValue);
+    private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
+            float startValue, float endValue);
+    private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
+            float endValue);
+    private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
+    private static native void nStart(long animatorSetPtr, VectorDrawableAnimator set);
+    private static native void nReverse(long animatorSetPtr, VectorDrawableAnimator set);
+    private static native void nEnd(long animatorSetPtr);
+    private static native void nReset(long animatorSetPtr);
 }
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 1fc1b83..bdbf3c0 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -39,6 +39,7 @@
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.util.VirtualRefBasePtr;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -47,6 +48,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Stack;
 
 /**
@@ -522,13 +524,13 @@
     public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
             @NonNull AttributeSet attrs, @Nullable Theme theme)
             throws XmlPullParserException, IOException {
-        if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererPtr != 0) {
+        if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererRefBase != null) {
             // This VD has been used to display other VD resource content, clean up.
             mVectorState.mRootGroup = new VGroup();
-            if (mVectorState.mNativeRendererPtr != 0) {
-                nDestroyRenderer(mVectorState.mNativeRendererPtr);
+            if (mVectorState.mNativeRendererRefBase != null) {
+                mVectorState.mNativeRendererRefBase.release();
             }
-            mVectorState.mNativeRendererPtr = nCreateRenderer(mVectorState.mRootGroup.mNativePtr);
+            mVectorState.createNativeRenderer(mVectorState.mRootGroup.mNativePtr);
         }
         final VectorDrawableState state = mVectorState;
         state.setDensity(Drawable.resolveDensity(r, 0));
@@ -707,7 +709,7 @@
         return mVectorState.mAutoMirrored;
     }
 
-    private static class VectorDrawableState extends ConstantState {
+    static class VectorDrawableState extends ConstantState {
         // Variables below need to be copied (deep copy if applicable) for mutation.
         int[] mThemeAttrs;
         int mChangingConfigurations;
@@ -722,7 +724,7 @@
         Insets mOpticalInsets = Insets.NONE;
         String mRootName = null;
         VGroup mRootGroup;
-        long mNativeRendererPtr;
+        VirtualRefBasePtr mNativeRendererRefBase = null;
 
         int mDensity = DisplayMetrics.DENSITY_DEFAULT;
         final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -743,7 +745,7 @@
                 mTintMode = copy.mTintMode;
                 mAutoMirrored = copy.mAutoMirrored;
                 mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
-                mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
+                createNativeRenderer(mRootGroup.mNativePtr);
 
                 mBaseWidth = copy.mBaseWidth;
                 mBaseHeight = copy.mBaseHeight;
@@ -758,18 +760,15 @@
             }
         }
 
-        @Override
-        public void finalize() throws Throwable {
-            if (mNativeRendererPtr != 0) {
-                nDestroyRenderer(mNativeRendererPtr);
-                mNativeRendererPtr = 0;
-            }
-            super.finalize();
+        private void createNativeRenderer(long rootGroupPtr) {
+            mNativeRendererRefBase = new VirtualRefBasePtr(nCreateRenderer(rootGroupPtr));
         }
 
-
         long getNativeRenderer() {
-            return mNativeRendererPtr;
+            if (mNativeRendererRefBase == null) {
+                return 0;
+            }
+            return mNativeRendererRefBase.get();
         }
 
         public boolean canReuseCache() {
@@ -808,7 +807,7 @@
 
         public VectorDrawableState() {
             mRootGroup = new VGroup();
-            mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
+            createNativeRenderer(mRootGroup.mNativePtr);
         }
 
         @Override
@@ -872,16 +871,16 @@
          * has changed.
          */
         public boolean setAlpha(float alpha) {
-            return nSetRootAlpha(mNativeRendererPtr, alpha);
+            return nSetRootAlpha(mNativeRendererRefBase.get(), alpha);
         }
 
         @SuppressWarnings("unused")
         public float getAlpha() {
-            return nGetRootAlpha(mNativeRendererPtr);
+            return nGetRootAlpha(mNativeRendererRefBase.get());
         }
     }
 
-    private static class VGroup implements VObject {
+    static class VGroup implements VObject {
         private static final int ROTATE_INDEX = 0;
         private static final int PIVOT_X_INDEX = 1;
         private static final int PIVOT_Y_INDEX = 2;
@@ -891,6 +890,28 @@
         private static final int TRANSLATE_Y_INDEX = 6;
         private static final int TRANSFORM_PROPERTY_COUNT = 7;
 
+        private static final HashMap<String, Integer> sPropertyMap =
+                new HashMap<String, Integer>() {
+                    {
+                        put("translateX", TRANSLATE_X_INDEX);
+                        put("translateY", TRANSLATE_Y_INDEX);
+                        put("scaleX", SCALE_X_INDEX);
+                        put("scaleY", SCALE_Y_INDEX);
+                        put("pivotX", PIVOT_X_INDEX);
+                        put("pivotY", PIVOT_Y_INDEX);
+                        put("rotation", ROTATE_INDEX);
+                    }
+                };
+
+        static int getPropertyIndex(String propertyName) {
+            if (sPropertyMap.containsKey(propertyName)) {
+                return sPropertyMap.get(propertyName);
+            } else {
+                // property not found
+                return -1;
+            }
+        }
+
         // Temp array to store transform values obtained from native.
         private float[] mTransform;
         /////////////////////////////////////////////////////
@@ -903,8 +924,11 @@
         private int mChangingConfigurations;
         private int[] mThemeAttrs;
         private String mGroupName = null;
-        private long mNativePtr = 0;
 
+        // The native object will be created in the constructor and will be destroyed in native
+        // when the neither java nor native has ref to the tree. This pointer should be valid
+        // throughout this VGroup Java object's life.
+        private final long mNativePtr;
         public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) {
 
             mIsStateful = copy.mIsStateful;
@@ -1044,16 +1068,6 @@
         }
 
         @Override
-        protected void finalize() throws Throwable {
-            if (mNativePtr != 0) {
-                nDestroy(mNativePtr);
-                mNativePtr = 0;
-            }
-            super.finalize();
-        }
-
-
-        @Override
         public void applyTheme(Theme t) {
             if (mThemeAttrs != null) {
                 final TypedArray a = t.resolveAttributes(mThemeAttrs,
@@ -1149,7 +1163,7 @@
     /**
      * Common Path information for clip path and normal path.
      */
-    private static abstract class VPath implements VObject {
+    static abstract class VPath implements VObject {
         protected PathParser.PathData mPathData = null;
 
         String mPathName;
@@ -1187,10 +1201,10 @@
      * Clip path, which only has name and pathData.
      */
     private static class VClipPath extends VPath {
-        long mNativePtr = 0;
+        private final long mNativePtr;
+
         public VClipPath() {
             mNativePtr = nCreateClipPath();
-            // Empty constructor.
         }
 
         public VClipPath(VClipPath copy) {
@@ -1204,14 +1218,6 @@
         }
 
         @Override
-        protected void finalize() throws Throwable {
-            if (mNativePtr != 0) {
-                nDestroy(mNativePtr);
-                mNativePtr = 0;
-            }
-            super.finalize();
-        }
-        @Override
         public void inflate(Resources r, AttributeSet attrs, Theme theme) {
             final TypedArray a = obtainAttributes(r, theme, attrs,
                     R.styleable.VectorDrawableClipPath);
@@ -1260,7 +1266,7 @@
     /**
      * Normal path, which contains all the fill / paint information.
      */
-    private static class VFullPath extends VPath {
+    static class VFullPath extends VPath {
         private static final int STROKE_WIDTH_INDEX = 0;
         private static final int STROKE_COLOR_INDEX = 1;
         private static final int STROKE_ALPHA_INDEX = 2;
@@ -1274,6 +1280,20 @@
         private static final int STROKE_MITER_LIMIT_INDEX = 10;
         private static final int TOTAL_PROPERTY_COUNT = 11;
 
+        private final static HashMap<String, Integer> sPropertyMap
+                = new HashMap<String, Integer> () {
+            {
+                put("strokeWidth", STROKE_WIDTH_INDEX);
+                put("strokeColor", STROKE_COLOR_INDEX);
+                put("strokeAlpha", STROKE_ALPHA_INDEX);
+                put("fillColor", FILL_COLOR_INDEX);
+                put("fillAlpha", FILL_ALPHA_INDEX);
+                put("trimPathStart", TRIM_PATH_START_INDEX);
+                put("trimPathEnd", TRIM_PATH_END_INDEX);
+                put("trimPathOffset", TRIM_PATH_OFFSET_INDEX);
+            }
+        };
+
         // Temp array to store property data obtained from native getter.
         private byte[] mPropertyData;
         /////////////////////////////////////////////////////
@@ -1282,10 +1302,9 @@
 
         ComplexColor mStrokeColors = null;
         ComplexColor mFillColors = null;
-        private long mNativePtr = 0;
+        private final long mNativePtr;
 
         public VFullPath() {
-            // Empty constructor.
             mNativePtr = nCreateFullPath();
         }
 
@@ -1297,6 +1316,14 @@
             mFillColors = copy.mFillColors;
         }
 
+        int getPropertyIndex(String propertyName) {
+            if (!sPropertyMap.containsKey(propertyName)) {
+                return -1;
+            } else {
+                return sPropertyMap.get(propertyName);
+            }
+        }
+
         @Override
         public boolean onStateChange(int[] stateSet) {
             boolean changed = false;
@@ -1341,15 +1368,6 @@
             a.recycle();
         }
 
-        @Override
-        protected void finalize() throws Throwable {
-            if (mNativePtr != 0) {
-                nDestroy(mNativePtr);
-                mNativePtr = 0;
-            }
-            super.finalize();
-        }
-
         private void updateStateFromTypedArray(TypedArray a) {
             int byteCount = TOTAL_PROPERTY_COUNT * 4;
             if (mPropertyData == null) {
@@ -1595,7 +1613,6 @@
     }
 
     private static native long nCreateRenderer(long rootGroupPtr);
-    private static native void nDestroyRenderer(long rendererPtr);
     private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
             float viewportHeight);
     private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
@@ -1605,7 +1622,7 @@
     private static native void nDraw(long rendererPtr, long canvasWrapperPtr,
             long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
     private static native long nCreateFullPath();
-    private static native long nCreateFullPath(long mNativeFullPathPtr);
+    private static native long nCreateFullPath(long nativeFullPathPtr);
     private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
             int length);
 
@@ -1621,7 +1638,6 @@
 
     private static native long nCreateGroup();
     private static native long nCreateGroup(long groupPtr);
-    private static native void nDestroy(long nodePtr);
     private static native void nSetName(long nodePtr, String name);
     private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
             int length);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 939b9f6..7b43947 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -78,6 +78,8 @@
     Program.cpp \
     ProgramCache.cpp \
     Properties.cpp \
+    PropertyValuesHolder.cpp \
+    PropertyValuesAnimatorSet.cpp \
     RenderBufferCache.cpp \
     RenderNode.cpp \
     RenderProperties.cpp \
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 5ca2a2f..7bd2b24 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -90,6 +90,9 @@
         doSetStartValue(getValue(mTarget));
     }
     if (mStagingPlayState > mPlayState) {
+        if (mStagingPlayState == PlayState::Restarted) {
+            mStagingPlayState = PlayState::Running;
+        }
         mPlayState = mStagingPlayState;
         // Oh boy, we're starting! Man the battle stations!
         if (mPlayState == PlayState::Running) {
@@ -131,6 +134,11 @@
         return true;
     }
 
+    // This should be set before setValue() so animators can query this time when setValue
+    // is called.
+    nsecs_t currentFrameTime = context.frameTimeMs();
+    onPlayTimeChanged(currentFrameTime - mStartTime);
+
     // If BaseRenderNodeAnimator is handling the delay (not typical), then
     // because the staging properties reflect the final value, we always need
     // to call setValue even if the animation isn't yet running or is still
@@ -141,8 +149,9 @@
     }
 
     float fraction = 1.0f;
+
     if (mPlayState == PlayState::Running && mDuration > 0) {
-        fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration;
+        fraction = (float)(currentFrameTime - mStartTime) / mDuration;
     }
     if (fraction >= 1.0f) {
         fraction = 1.0f;
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index aea95bf..2c9c9c3 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -59,7 +59,13 @@
         mMayRunAsync = mayRunAsync;
     }
     bool mayRunAsync() { return mMayRunAsync; }
-    ANDROID_API void start() { mStagingPlayState = PlayState::Running; onStagingPlayStateChanged(); }
+    ANDROID_API void start() {
+        if (mStagingPlayState == PlayState::NotStarted) {
+            mStagingPlayState = PlayState::Running;
+        } else {
+            mStagingPlayState = PlayState::Restarted;
+        }
+        onStagingPlayStateChanged(); }
     ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); }
 
     void attach(RenderNode* target);
@@ -77,10 +83,27 @@
     void forceEndNow(AnimationContext& context);
 
 protected:
+    // PlayState is used by mStagingPlayState and mPlayState to track the state initiated from UI
+    // thread and Render Thread animation state, respectively.
+    // From the UI thread, mStagingPlayState transition looks like
+    // NotStarted -> Running -> Finished
+    //                ^            |
+    //                |            |
+    //            Restarted <------
+    // Note: For mStagingState, the Finished state (optional) is only set when the animation is
+    // terminated by user.
+    //
+    // On Render Thread, mPlayState transition:
+    // NotStart -> Running -> Finished
+    //                ^            |
+    //                |            |
+    //                -------------
+
     enum class PlayState {
         NotStarted,
         Running,
         Finished,
+        Restarted,
     };
 
     BaseRenderNodeAnimator(float finalValue);
@@ -93,6 +116,7 @@
     void callOnFinishedListener(AnimationContext& context);
 
     virtual void onStagingPlayStateChanged() {}
+    virtual void onPlayTimeChanged(nsecs_t playTime) {}
 
     RenderNode* mTarget;
 
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index dbd502d..27facdf 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -52,6 +52,13 @@
 
 } // namespace SaveFlags
 
+namespace uirenderer {
+namespace VectorDrawable {
+class Tree;
+};
+};
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
 class ANDROID_API Canvas {
 public:
     virtual ~Canvas() {};
@@ -217,6 +224,11 @@
      */
     virtual bool drawTextAbsolutePos() const = 0;
 
+    /**
+     * Draws a VectorDrawable onto the canvas.
+     */
+    virtual void drawVectorDrawable(VectorDrawableRoot* tree);
+
 protected:
     void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
 };
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 7eaa785..3db14b5 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -16,11 +16,12 @@
 
 #include "DisplayListCanvas.h"
 
-#include "ResourceCache.h"
 #include "DeferredDisplayList.h"
 #include "DeferredLayerUpdater.h"
 #include "DisplayListOp.h"
+#include "ResourceCache.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 #include "utils/PaintUtils.h"
 
 #include <SkCamera.h>
@@ -412,6 +413,16 @@
     addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint)));
 }
 
+void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    mDisplayList->ref(tree);
+    const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty();
+    SkPaint* paint = tree->getPaint();
+    const SkRect bounds = tree->getBounds();
+    addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
+            0, 0, bitmap.width(), bitmap.height(),
+            bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint)));
+}
+
 void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
         const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
     if (!glyphs || count <= 0) return;
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index ad93960..06e72a0 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -206,6 +206,8 @@
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) override;
 
+    virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
+
     // Text
     virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
@@ -214,7 +216,6 @@
             float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
 
-
 private:
 
     CanvasState mState;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index c8910cb..185acce 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -19,6 +19,7 @@
 #include "Canvas.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 #include "renderstate/OffscreenBufferPool.h"
 #include "utils/FatVector.h"
 #include "utils/PaintUtils.h"
@@ -545,6 +546,18 @@
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
+void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
+    const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
+    SkPaint* paint = op.vectorDrawable->getPaint();
+    const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds,
+            op.localMatrix,
+            op.localClip,
+            paint,
+            &bitmap,
+            Rect(bitmap.width(), bitmap.height()));
+    deferBitmapRectOp(*resolvedOp);
+}
+
 void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
     // allocate a temporary oval op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
new file mode 100644
index 0000000..eca1afcc
--- /dev/null
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PropertyValuesAnimatorSet.h"
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
+            Interpolator* interpolator, nsecs_t startDelay,
+            nsecs_t duration, int repeatCount) {
+
+    PropertyAnimator* animator = new PropertyAnimator(propertyValuesHolder,
+            interpolator, startDelay, duration, repeatCount);
+    mAnimators.emplace_back(animator);
+    setListener(new PropertyAnimatorSetListener(this));
+}
+
+PropertyValuesAnimatorSet::PropertyValuesAnimatorSet()
+        : BaseRenderNodeAnimator(1.0f) {
+    setStartValue(0);
+    mLastFraction = 0.0f;
+    setInterpolator(new LinearInterpolator());
+}
+
+void PropertyValuesAnimatorSet::onFinished(BaseRenderNodeAnimator* animator) {
+    if (mOneShotListener.get()) {
+        mOneShotListener->onAnimationFinished(animator);
+        mOneShotListener = nullptr;
+    }
+}
+
+float PropertyValuesAnimatorSet::getValue(RenderNode* target) const {
+    return mLastFraction;
+}
+
+void PropertyValuesAnimatorSet::setValue(RenderNode* target, float value) {
+    mLastFraction = value;
+}
+
+void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) {
+    for (size_t i = 0; i < mAnimators.size(); i++) {
+        mAnimators[i]->setCurrentPlayTime(playTime);
+    }
+}
+
+void PropertyValuesAnimatorSet::reset() {
+    // TODO: implement reset through adding a play state because we need to support reset() even
+    // during an animation run.
+}
+
+void PropertyValuesAnimatorSet::start(AnimationListener* listener) {
+    init();
+    mOneShotListener = listener;
+    BaseRenderNodeAnimator::start();
+}
+
+void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) {
+// TODO: implement reverse
+}
+
+void PropertyValuesAnimatorSet::init() {
+    if (mInitialized) {
+        return;
+    }
+    nsecs_t maxDuration = 0;
+    for (size_t i = 0; i < mAnimators.size(); i++) {
+        if (maxDuration < mAnimators[i]->getTotalDuration()) {
+            maxDuration = mAnimators[i]->getTotalDuration();
+        }
+    }
+    mDuration = maxDuration;
+    mInitialized = true;
+}
+
+uint32_t PropertyValuesAnimatorSet::dirtyMask() {
+    return RenderNode::DISPLAY_LIST;
+}
+
+PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator,
+        nsecs_t startDelay, nsecs_t duration, int repeatCount)
+        : mPropertyValuesHolder(holder), mInterpolator(interpolator), mStartDelay(startDelay),
+          mDuration(duration) {
+    if (repeatCount < 0) {
+        mRepeatCount = UINT32_MAX;
+    } else {
+        mRepeatCount = repeatCount;
+    }
+    mTotalDuration = ((nsecs_t) mRepeatCount + 1) * mDuration + mStartDelay;
+}
+
+void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) {
+    if (playTime >= mStartDelay && playTime < mTotalDuration) {
+         nsecs_t currentIterationPlayTime = (playTime - mStartDelay) % mDuration;
+         mLatestFraction = currentIterationPlayTime / (float) mDuration;
+    } else if (mLatestFraction < 1.0f && playTime >= mTotalDuration) {
+        mLatestFraction = 1.0f;
+    } else {
+        return;
+    }
+
+    setFraction(mLatestFraction);
+}
+
+void PropertyAnimator::setFraction(float fraction) {
+    float interpolatedFraction = mInterpolator->interpolate(mLatestFraction);
+    mPropertyValuesHolder->setFraction(interpolatedFraction);
+}
+
+void PropertyAnimatorSetListener::onAnimationFinished(BaseRenderNodeAnimator* animator) {
+    mSet->onFinished(animator);
+}
+
+}
+}
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
new file mode 100644
index 0000000..4c7ce52
--- /dev/null
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Animator.h"
+#include "PropertyValuesHolder.h"
+#include "Interpolator.h"
+
+namespace android {
+namespace uirenderer {
+
+class PropertyAnimator {
+public:
+    PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, nsecs_t startDelay,
+            nsecs_t duration, int repeatCount);
+    void setCurrentPlayTime(nsecs_t playTime);
+    nsecs_t getTotalDuration() {
+        return mTotalDuration;
+    }
+    void setFraction(float fraction);
+
+private:
+    std::unique_ptr<PropertyValuesHolder> mPropertyValuesHolder;
+    std::unique_ptr<Interpolator> mInterpolator;
+    nsecs_t mStartDelay;
+    nsecs_t mDuration;
+    uint32_t mRepeatCount;
+    nsecs_t mTotalDuration;
+    float mLatestFraction = 0.0f;
+};
+
+class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator {
+public:
+    friend class PropertyAnimatorSetListener;
+    PropertyValuesAnimatorSet();
+
+    void start(AnimationListener* listener);
+    void reverse(AnimationListener* listener);
+    void reset();
+
+    void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
+            Interpolator* interpolators, int64_t startDelays,
+            nsecs_t durations, int repeatCount);
+    virtual uint32_t dirtyMask();
+
+protected:
+    virtual float getValue(RenderNode* target) const override;
+    virtual void setValue(RenderNode* target, float value) override;
+    virtual void onPlayTimeChanged(nsecs_t playTime) override;
+
+private:
+    void init();
+    void onFinished(BaseRenderNodeAnimator* animator);
+    // Listener set from outside
+    sp<AnimationListener> mOneShotListener;
+    std::vector< std::unique_ptr<PropertyAnimator> > mAnimators;
+    float mLastFraction = 0.0f;
+    bool mInitialized = false;
+};
+
+class PropertyAnimatorSetListener : public AnimationListener {
+public:
+    PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {}
+    virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) override;
+
+private:
+    PropertyValuesAnimatorSet* mSet;
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp
new file mode 100644
index 0000000..8f837f6
--- /dev/null
+++ b/libs/hwui/PropertyValuesHolder.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PropertyValuesHolder.h"
+
+#include "utils/VectorDrawableUtils.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+using namespace VectorDrawable;
+
+float PropertyValuesHolder::getValueFromData(float fraction) {
+    if (mDataSource.size() == 0) {
+        LOG_ALWAYS_FATAL("No data source is defined");
+        return 0;
+    }
+    if (fraction <= 0.0f) {
+        return mDataSource.front();
+    }
+    if (fraction >= 1.0f) {
+        return mDataSource.back();
+    }
+
+    fraction *= mDataSource.size() - 1;
+    int lowIndex = floor(fraction);
+    fraction -= lowIndex;
+
+    float value = mDataSource[lowIndex] * (1.0f - fraction)
+            + mDataSource[lowIndex + 1] * fraction;
+    return value;
+}
+
+void GroupPropertyValuesHolder::setFraction(float fraction) {
+    float animatedValue;
+    if (mDataSource.size() > 0) {
+        animatedValue = getValueFromData(fraction);
+    } else {
+        animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+    }
+    mGroup->setPropertyValue(mPropertyId, animatedValue);
+}
+
+inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
+    return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
+}
+
+// TODO: Add a test for this
+SkColor FullPathColorPropertyValuesHolder::interpolateColors(SkColor fromColor, SkColor toColor,
+        float fraction) {
+    U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
+    U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
+    U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
+    U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
+    return SkColorSetARGB(alpha, red, green, blue);
+}
+
+void FullPathColorPropertyValuesHolder::setFraction(float fraction) {
+    SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction);
+    mFullPath->setColorPropertyValue(mPropertyId, animatedValue);
+}
+
+void FullPathPropertyValuesHolder::setFraction(float fraction) {
+    float animatedValue;
+    if (mDataSource.size() > 0) {
+        animatedValue = getValueFromData(fraction);
+    } else {
+        animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+    }
+    mFullPath->setPropertyValue(mPropertyId, animatedValue);
+}
+
+void PathDataPropertyValuesHolder::setFraction(float fraction) {
+    VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction);
+    mPath->setPathData(mPathData);
+}
+
+void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
+    float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+    mTree->setRootAlpha(animatedValue);
+}
+
+} // namepace uirenderer
+} // namespace android
diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h
new file mode 100644
index 0000000..b905fae
--- /dev/null
+++ b/libs/hwui/PropertyValuesHolder.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "VectorDrawable.h"
+
+#include <SkColor.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * PropertyValues holder contains data needed to change a property of a Vector Drawable object.
+ * When a fraction in [0f, 1f] is provided, the holder will calculate an interpolated value based
+ * on its start and end value, and set the new value on the VectorDrawble's corresponding property.
+ */
+class ANDROID_API PropertyValuesHolder {
+public:
+    virtual void setFraction(float fraction) = 0;
+    void setPropertyDataSource(float* dataSource, int length) {
+        mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
+    }
+   float getValueFromData(float fraction);
+   virtual ~PropertyValuesHolder() {}
+protected:
+   std::vector<float> mDataSource;
+};
+
+class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue,
+            float endValue)
+            : mGroup(ptr)
+            , mPropertyId(propertyId)
+            , mStartValue(startValue)
+            , mEndValue(endValue){
+    }
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::Group* mGroup;
+    int mPropertyId;
+    float mStartValue;
+    float mEndValue;
+};
+
+class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, int32_t startValue,
+            int32_t endValue)
+            : mFullPath(ptr)
+            , mPropertyId(propertyId)
+            , mStartValue(startValue)
+            , mEndValue(endValue) {};
+    void setFraction(float fraction) override;
+    static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction);
+private:
+    VectorDrawable::FullPath* mFullPath;
+    int mPropertyId;
+    int32_t mStartValue;
+    int32_t mEndValue;
+};
+
+class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue,
+            float endValue)
+            : mFullPath(ptr)
+            , mPropertyId(propertyId)
+            , mStartValue(startValue)
+            , mEndValue(endValue) {};
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::FullPath* mFullPath;
+    int mPropertyId;
+    float mStartValue;
+    float mEndValue;
+};
+
+class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue,
+            PathData* endValue)
+            : mPath(ptr)
+            , mStartValue(*startValue)
+            , mEndValue(*endValue) {};
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::Path* mPath;
+    PathData mPathData;
+    PathData mStartValue;
+    PathData mEndValue;
+};
+
+class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue)
+            : mTree(tree)
+            , mStartValue(startValue)
+            , mEndValue(endValue) {}
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::Tree* mTree;
+    float mStartValue;
+    float mEndValue;
+};
+}
+}
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 593d690..bb26e2e 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HWUI_RECORDED_OP_H
 #define ANDROID_HWUI_RECORDED_OP_H
 
+#include "RecordedOp.h"
 #include "font/FontUtil.h"
 #include "Matrix.h"
 #include "Rect.h"
@@ -39,6 +40,10 @@
 class RenderNode;
 struct Vertex;
 
+namespace VectorDrawable {
+class Tree;
+}
+
 /**
  * Authoritative op list, used for generating the op ID enum, ID based LUTS, and
  * the functions to which they dispatch. Parameter macros are executed for each op,
@@ -75,6 +80,7 @@
         PRE_RENDER_OP_FN(EndLayerOp) \
         PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \
         PRE_RENDER_OP_FN(EndUnclippedLayerOp) \
+        PRE_RENDER_OP_FN(VectorDrawableOp) \
         \
         RENDER_ONLY_OP_FN(ShadowOp) \
         RENDER_ONLY_OP_FN(LayerOp) \
@@ -325,6 +331,13 @@
     const float* ry;
 };
 
+struct VectorDrawableOp : RecordedOp {
+    VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS)
+            : SUPER_PAINTLESS(VectorDrawableOp)
+            , vectorDrawable(tree) {}
+    VectorDrawable::Tree* vectorDrawable;
+};
+
 /**
  * Real-time, dynamic-lit shadow.
  *
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 2387962..16929b8 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -19,6 +19,7 @@
 #include "DeferredLayerUpdater.h"
 #include "RecordedOp.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -395,7 +396,6 @@
             &x->value, &y->value, &radius->value));
 }
 
-
 void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
     addOp(new (alloc()) OvalOp(
             Rect(left, top, right, bottom),
@@ -422,6 +422,15 @@
             refPaint(&paint), refPath(&path)));
 }
 
+void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    mDisplayList->ref(tree);
+    addOp(new (alloc()) VectorDrawableOp(
+            tree,
+            Rect(tree->getBounds()),
+            *(mState.currentSnapshot()->transform),
+            getRecordedClip()));
+}
+
 // Bitmap-based
 void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
     save(SaveFlags::Matrix);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 375760f..cc14e61 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -175,6 +175,8 @@
             const uint16_t* indices, int indexCount, const SkPaint& paint) override
         { /* RecordingCanvas does not support drawVertices(); ignore */ }
 
+    virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
+
     // Bitmap-based
     virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
     virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d320a41..bd4442d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -32,6 +32,8 @@
 #include <SkTLazy.h>
 #include <SkTemplates.h>
 
+#include "VectorDrawable.h"
+
 #include <memory>
 
 namespace android {
@@ -153,6 +155,7 @@
             float hOffset, float vOffset, const SkPaint& paint) override;
 
     virtual bool drawTextAbsolutePos() const  override { return true; }
+    virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
     virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
             uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
@@ -742,6 +745,14 @@
     NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr);
 }
 
+void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
+    const SkBitmap& bitmap = vectorDrawable->getBitmapUpdateIfDirty();
+    SkRect bounds = vectorDrawable->getBounds();
+    drawBitmap(bitmap, 0, 0, bitmap.width(), bitmap.height(),
+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+            vectorDrawable->getPaint());
+}
+
 // ----------------------------------------------------------------------------
 // Canvas draw operations: Text
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 1cf15ac..2e3856f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -138,18 +138,7 @@
 }
 
 FullPath::FullPath(const FullPath& path) : Path(path) {
-    mStrokeWidth = path.mStrokeWidth;
-    mStrokeColor = path.mStrokeColor;
-    mStrokeAlpha = path.mStrokeAlpha;
-    mFillColor = path.mFillColor;
-    mFillAlpha = path.mFillAlpha;
-    mTrimPathStart = path.mTrimPathStart;
-    mTrimPathEnd = path.mTrimPathEnd;
-    mTrimPathOffset = path.mTrimPathOffset;
-    mStrokeMiterLimit = path.mStrokeMiterLimit;
-    mStrokeLineCap = path.mStrokeLineCap;
-    mStrokeLineJoin = path.mStrokeLineJoin;
-
+    mProperties = path.mProperties;
     SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient);
     SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient);
 }
@@ -159,7 +148,7 @@
         return mTrimmedSkPath;
     }
     Path::getUpdatedPath();
-    if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+    if (mProperties.trimPathStart != 0.0f || mProperties.trimPathEnd != 1.0f) {
         applyTrim();
         return mTrimmedSkPath;
     } else {
@@ -170,14 +159,14 @@
 void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
         SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
         float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin) {
-    mStrokeWidth = strokeWidth;
-    mStrokeColor = strokeColor;
-    mStrokeAlpha = strokeAlpha;
-    mFillColor = fillColor;
-    mFillAlpha = fillAlpha;
-    mStrokeMiterLimit = strokeMiterLimit;
-    mStrokeLineCap = SkPaint::Cap(strokeLineCap);
-    mStrokeLineJoin = SkPaint::Join(strokeLineJoin);
+    mProperties.strokeWidth = strokeWidth;
+    mProperties.strokeColor = strokeColor;
+    mProperties.strokeAlpha = strokeAlpha;
+    mProperties.fillColor = fillColor;
+    mProperties.fillAlpha = fillAlpha;
+    mProperties.strokeMiterLimit = strokeMiterLimit;
+    mProperties.strokeLineCap = strokeLineCap;
+    mProperties.strokeLineJoin = strokeLineJoin;
 
     // If any trim property changes, mark trim dirty and update the trim path
     setTrimPathStart(trimPathStart);
@@ -195,12 +184,12 @@
     // Draw path's fill, if fill color or gradient is valid
     bool needsFill = false;
     if (mFillGradient != nullptr) {
-        mPaint.setColor(applyAlpha(SK_ColorBLACK, mFillAlpha));
+        mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.fillAlpha));
         SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix);
         mPaint.setShader(newShader);
         needsFill = true;
-    } else if (mFillColor != SK_ColorTRANSPARENT) {
-        mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+    } else if (mProperties.fillColor != SK_ColorTRANSPARENT) {
+        mPaint.setColor(applyAlpha(mProperties.fillColor, mProperties.fillAlpha));
         needsFill = true;
     }
 
@@ -213,21 +202,21 @@
     // Draw path's stroke, if stroke color or gradient is valid
     bool needsStroke = false;
     if (mStrokeGradient != nullptr) {
-        mPaint.setColor(applyAlpha(SK_ColorBLACK, mStrokeAlpha));
+        mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.strokeAlpha));
         SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix);
         mPaint.setShader(newShader);
         needsStroke = true;
-    } else if (mStrokeColor != SK_ColorTRANSPARENT) {
-        mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+    } else if (mProperties.strokeColor != SK_ColorTRANSPARENT) {
+        mPaint.setColor(applyAlpha(mProperties.strokeColor, mProperties.strokeAlpha));
         needsStroke = true;
     }
     if (needsStroke) {
         mPaint.setStyle(SkPaint::Style::kStroke_Style);
         mPaint.setAntiAlias(true);
-        mPaint.setStrokeJoin(mStrokeLineJoin);
-        mPaint.setStrokeCap(mStrokeLineCap);
-        mPaint.setStrokeMiter(mStrokeMiterLimit);
-        mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
+        mPaint.setStrokeJoin(SkPaint::Join(mProperties.strokeLineJoin));
+        mPaint.setStrokeCap(SkPaint::Cap(mProperties.strokeLineCap));
+        mPaint.setStrokeMiter(mProperties.strokeMiterLimit);
+        mPaint.setStrokeWidth(mProperties.strokeWidth * strokeScale);
         outCanvas->drawPath(renderPath, mPaint);
     }
 }
@@ -236,14 +225,14 @@
  * Applies trimming to the specified path.
  */
 void FullPath::applyTrim() {
-    if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
+    if (mProperties.trimPathStart == 0.0f && mProperties.trimPathEnd == 1.0f) {
         // No trimming necessary.
         return;
     }
     SkPathMeasure measure(mSkPath, false);
     float len = SkScalarToFloat(measure.getLength());
-    float start = len * fmod((mTrimPathStart + mTrimPathOffset), 1.0f);
-    float end = len * fmod((mTrimPathEnd + mTrimPathOffset), 1.0f);
+    float start = len * fmod((mProperties.trimPathStart + mProperties.trimPathOffset), 1.0f);
+    float end = len * fmod((mProperties.trimPathEnd + mProperties.trimPathOffset), 1.0f);
 
     mTrimmedSkPath.reset();
     if (start > end) {
@@ -255,76 +244,69 @@
     mTrimDirty = false;
 }
 
-inline int putData(int8_t* outBytes, int startIndex, float value) {
-    int size = sizeof(float);
-    memcpy(&outBytes[startIndex], &value, size);
-    return size;
-}
-
-inline int putData(int8_t* outBytes, int startIndex, int value) {
-    int size = sizeof(int);
-    memcpy(&outBytes[startIndex], &value, size);
-    return size;
-}
-
-struct FullPathProperties {
-    // TODO: Consider storing full path properties in this struct instead of the fields.
-    float strokeWidth;
-    SkColor strokeColor;
-    float strokeAlpha;
-    SkColor fillColor;
-    float fillAlpha;
-    float trimPathStart;
-    float trimPathEnd;
-    float trimPathOffset;
-    int32_t strokeLineCap;
-    int32_t strokeLineJoin;
-    float strokeMiterLimit;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FullPathProperties);
+REQUIRE_COMPATIBLE_LAYOUT(FullPath::Properties);
 
 static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
 static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
 
 bool FullPath::getProperties(int8_t* outProperties, int length) {
-    int propertyDataSize = sizeof(FullPathProperties);
+    int propertyDataSize = sizeof(Properties);
     if (length != propertyDataSize) {
         LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
                 propertyDataSize, length);
         return false;
     }
-    // TODO: consider replacing the property fields with a FullPathProperties struct.
-    FullPathProperties properties;
-    properties.strokeWidth = mStrokeWidth;
-    properties.strokeColor = mStrokeColor;
-    properties.strokeAlpha = mStrokeAlpha;
-    properties.fillColor = mFillColor;
-    properties.fillAlpha = mFillAlpha;
-    properties.trimPathStart = mTrimPathStart;
-    properties.trimPathEnd = mTrimPathEnd;
-    properties.trimPathOffset = mTrimPathOffset;
-    properties.strokeLineCap = mStrokeLineCap;
-    properties.strokeLineJoin = mStrokeLineJoin;
-    properties.strokeMiterLimit = mStrokeMiterLimit;
-
-    memcpy(outProperties, &properties, length);
+    Properties* out = reinterpret_cast<Properties*>(outProperties);
+    *out = mProperties;
     return true;
 }
 
+void FullPath::setColorPropertyValue(int propertyId, int32_t value) {
+    Property currentProperty = static_cast<Property>(propertyId);
+    if (currentProperty == Property::StrokeColor) {
+        mProperties.strokeColor = value;
+    } else if (currentProperty == Property::FillColor) {
+        mProperties.fillColor = value;
+    } else {
+        LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property with id: %d",
+                propertyId);
+    }
+}
+
+void FullPath::setPropertyValue(int propertyId, float value) {
+    Property property = static_cast<Property>(propertyId);
+    switch (property) {
+    case Property::StrokeWidth:
+        setStrokeWidth(value);
+        break;
+    case Property::StrokeAlpha:
+        setStrokeAlpha(value);
+        break;
+    case Property::FillAlpha:
+        setFillAlpha(value);
+        break;
+    case Property::TrimPathStart:
+        setTrimPathStart(value);
+        break;
+    case Property::TrimPathEnd:
+        setTrimPathEnd(value);
+        break;
+    case Property::TrimPathOffset:
+        setTrimPathOffset(value);
+        break;
+    default:
+        LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId);
+        break;
+    }
+}
+
 void ClipPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
         float strokeScale, const SkMatrix& matrix){
     outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
 }
 
 Group::Group(const Group& group) : Node(group) {
-    mRotate = group.mRotate;
-    mPivotX = group.mPivotX;
-    mPivotY = group.mPivotY;
-    mScaleX = group.mScaleX;
-    mScaleY = group.mScaleY;
-    mTranslateX = group.mTranslateX;
-    mTranslateY = group.mTranslateY;
+    mProperties = group.mProperties;
 }
 
 void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
@@ -342,7 +324,7 @@
     // Save the current clip information, which is local to this group.
     outCanvas->save();
     // Draw the group tree in the same order as the XML file.
-    for (Node* child : mChildren) {
+    for (auto& child : mChildren) {
         child->draw(outCanvas, stackedMatrix, scaleX, scaleY);
     }
     // Restore the previous clip information.
@@ -371,14 +353,15 @@
     outMatrix->reset();
     // TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
     // translating to pivot for rotating and scaling, then translating back.
-    outMatrix->postTranslate(-mPivotX, -mPivotY);
-    outMatrix->postScale(mScaleX, mScaleY);
-    outMatrix->postRotate(mRotate, 0, 0);
-    outMatrix->postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+    outMatrix->postTranslate(-mProperties.pivotX, -mProperties.pivotY);
+    outMatrix->postScale(mProperties.scaleX, mProperties.scaleY);
+    outMatrix->postRotate(mProperties.rotate, 0, 0);
+    outMatrix->postTranslate(mProperties.translateX + mProperties.pivotX,
+            mProperties.translateY + mProperties.pivotY);
 }
 
 void Group::addChild(Node* child) {
-    mChildren.push_back(child);
+    mChildren.emplace_back(child);
 }
 
 bool Group::getProperties(float* outProperties, int length) {
@@ -388,38 +371,68 @@
                 propertyCount, length);
         return false;
     }
-    for (int i = 0; i < propertyCount; i++) {
-        Property currentProperty = static_cast<Property>(i);
-        switch (currentProperty) {
-        case Property::Rotate_Property:
-            outProperties[i] = mRotate;
-            break;
-        case Property::PivotX_Property:
-            outProperties[i] = mPivotX;
-            break;
-        case Property::PivotY_Property:
-            outProperties[i] = mPivotY;
-            break;
-        case Property::ScaleX_Property:
-            outProperties[i] = mScaleX;
-            break;
-        case Property::ScaleY_Property:
-            outProperties[i] = mScaleY;
-            break;
-        case Property::TranslateX_Property:
-            outProperties[i] = mTranslateX;
-            break;
-        case Property::TranslateY_Property:
-            outProperties[i] = mTranslateY;
-            break;
-        default:
-            LOG_ALWAYS_FATAL("Invalid input index: %d", i);
-            return false;
-        }
-    }
+    Properties* out = reinterpret_cast<Properties*>(outProperties);
+    *out = mProperties;
     return true;
 }
 
+// TODO: Consider animating the properties as float pointers
+float Group::getPropertyValue(int propertyId) const {
+    Property currentProperty = static_cast<Property>(propertyId);
+    switch (currentProperty) {
+    case Property::Rotate:
+        return mProperties.rotate;
+    case Property::PivotX:
+        return mProperties.pivotX;
+    case Property::PivotY:
+        return mProperties.pivotY;
+    case Property::ScaleX:
+        return mProperties.scaleX;
+    case Property::ScaleY:
+        return mProperties.scaleY;
+    case Property::TranslateX:
+        return mProperties.translateX;
+    case Property::TranslateY:
+        return mProperties.translateY;
+    default:
+        LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+        return 0;
+    }
+}
+
+void Group::setPropertyValue(int propertyId, float value) {
+    Property currentProperty = static_cast<Property>(propertyId);
+    switch (currentProperty) {
+    case Property::Rotate:
+        mProperties.rotate = value;
+        break;
+    case Property::PivotX:
+        mProperties.pivotX = value;
+        break;
+    case Property::PivotY:
+        mProperties.pivotY = value;
+        break;
+    case Property::ScaleX:
+        mProperties.scaleX = value;
+        break;
+    case Property::ScaleY:
+        mProperties.scaleY = value;
+        break;
+    case Property::TranslateX:
+        mProperties.translateX = value;
+        break;
+    case Property::TranslateY:
+        mProperties.translateY = value;
+        break;
+    default:
+        LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+    }
+}
+
+bool Group::isValidProperty(int propertyId) {
+    return propertyId >= 0 && propertyId < static_cast<int>(Property::Count);
+}
+
 void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
         const SkRect& bounds, bool needsMirroring, bool canReuseCache) {
     // The imageView can scale the canvas in different ways, in order to
@@ -445,6 +458,8 @@
         return;
     }
 
+    mPaint.setColorFilter(colorFilter);
+
     int saveCount = outCanvas->save(SaveFlags::MatrixClip);
     outCanvas->translate(mBounds.fLeft, mBounds.fTop);
 
@@ -458,43 +473,33 @@
     // And we use this bound for the destination rect for the drawBitmap, so
     // we offset to (0, 0);
     mBounds.offsetTo(0, 0);
-
     createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
-    if (!mAllowCaching) {
-        updateCachedBitmap(scaledWidth, scaledHeight);
-    } else {
-        if (!canReuseCache || mCacheDirty) {
-            updateCachedBitmap(scaledWidth, scaledHeight);
-        }
-    }
-    drawCachedBitmapWithRootAlpha(outCanvas, colorFilter, mBounds);
+
+    outCanvas->drawVectorDrawable(this);
 
     outCanvas->restoreToCount(saveCount);
 }
 
-void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
-        const SkRect& originalBounds) {
+SkPaint* Tree::getPaint() {
     SkPaint* paint;
-    if (mRootAlpha == 1.0f && filter == NULL) {
+    if (mRootAlpha == 1.0f && mPaint.getColorFilter() == NULL) {
         paint = NULL;
     } else {
         mPaint.setFilterQuality(kLow_SkFilterQuality);
         mPaint.setAlpha(mRootAlpha * 255);
-        mPaint.setColorFilter(filter);
         paint = &mPaint;
     }
-    outCanvas->drawBitmap(mCachedBitmap, 0, 0, mCachedBitmap.width(), mCachedBitmap.height(),
-            originalBounds.fLeft, originalBounds.fTop, originalBounds.fRight,
-            originalBounds.fBottom, paint);
+    return paint;
 }
 
-void Tree::updateCachedBitmap(int width, int height) {
+const SkBitmap& Tree::getBitmapUpdateIfDirty() {
     mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
     SkCanvas outCanvas(mCachedBitmap);
-    float scaleX = width / mViewportWidth;
-    float scaleY = height / mViewportHeight;
+    float scaleX = (float) mCachedBitmap.width() / mViewportWidth;
+    float scaleY = (float) mCachedBitmap.height() / mViewportHeight;
     mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY);
     mCacheDirty = false;
+    return mCachedBitmap;
 }
 
 void Tree::createCachedBitmapIfNeeded(int width, int height) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 09bdce5..36a8aeb 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,6 +18,7 @@
 #define ANDROID_HWUI_VPATH_H
 
 #include "Canvas.h"
+
 #include <SkBitmap.h>
 #include <SkColor.h>
 #include <SkCanvas.h>
@@ -104,6 +105,21 @@
 
 class ANDROID_API FullPath: public Path {
 public:
+
+struct Properties {
+    float strokeWidth = 0;
+    SkColor strokeColor = SK_ColorTRANSPARENT;
+    float strokeAlpha = 1;
+    SkColor fillColor = SK_ColorTRANSPARENT;
+    float fillAlpha = 1;
+    float trimPathStart = 0;
+    float trimPathEnd = 1;
+    float trimPathOffset = 0;
+    int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
+    int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
+    float strokeMiterLimit = 4;
+};
+
     FullPath(const FullPath& path); // for cloning
     FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
     FullPath() : Path() {}
@@ -118,55 +134,58 @@
             float strokeAlpha, SkColor fillColor, float fillAlpha,
             float trimPathStart, float trimPathEnd, float trimPathOffset,
             float strokeMiterLimit, int strokeLineCap, int strokeLineJoin);
+    // TODO: Cleanup: Remove the setter and getters below, and their counterparts in java and JNI
     float getStrokeWidth() {
-        return mStrokeWidth;
+        return mProperties.strokeWidth;
     }
     void setStrokeWidth(float strokeWidth) {
-        mStrokeWidth = strokeWidth;
+        mProperties.strokeWidth = strokeWidth;
     }
     SkColor getStrokeColor() {
-        return mStrokeColor;
+        return mProperties.strokeColor;
     }
     void setStrokeColor(SkColor strokeColor) {
-        mStrokeColor = strokeColor;
+        mProperties.strokeColor = strokeColor;
     }
     float getStrokeAlpha() {
-        return mStrokeAlpha;
+        return mProperties.strokeAlpha;
     }
     void setStrokeAlpha(float strokeAlpha) {
-        mStrokeAlpha = strokeAlpha;
+        mProperties.strokeAlpha = strokeAlpha;
     }
     SkColor getFillColor() {
-        return mFillColor;
+        return mProperties.fillColor;
     }
     void setFillColor(SkColor fillColor) {
-        mFillColor = fillColor;
+        mProperties.fillColor = fillColor;
     }
     float getFillAlpha() {
-        return mFillAlpha;
+        return mProperties.fillAlpha;
     }
     void setFillAlpha(float fillAlpha) {
-        mFillAlpha = fillAlpha;
+        mProperties.fillAlpha = fillAlpha;
     }
     float getTrimPathStart() {
-        return mTrimPathStart;
+        return mProperties.trimPathStart;
     }
     void setTrimPathStart(float trimPathStart) {
-        VD_SET_PROP_WITH_FLAG(mTrimPathStart, trimPathStart, mTrimDirty);
+        VD_SET_PROP_WITH_FLAG(mProperties.trimPathStart, trimPathStart, mTrimDirty);
     }
     float getTrimPathEnd() {
-        return mTrimPathEnd;
+        return mProperties.trimPathEnd;
     }
     void setTrimPathEnd(float trimPathEnd) {
-        VD_SET_PROP_WITH_FLAG(mTrimPathEnd, trimPathEnd, mTrimDirty);
+        VD_SET_PROP_WITH_FLAG(mProperties.trimPathEnd, trimPathEnd, mTrimDirty);
     }
     float getTrimPathOffset() {
-        return mTrimPathOffset;
+        return mProperties.trimPathOffset;
     }
     void setTrimPathOffset(float trimPathOffset) {
-        VD_SET_PROP_WITH_FLAG(mTrimPathOffset, trimPathOffset, mTrimDirty);
+        VD_SET_PROP_WITH_FLAG(mProperties.trimPathOffset, trimPathOffset, mTrimDirty);
     }
     bool getProperties(int8_t* outProperties, int length);
+    void setColorPropertyValue(int propertyId, int32_t value);
+    void setPropertyValue(int propertyId, float value);
 
     void setFillGradient(SkShader* fillGradient) {
         SkRefCnt_SafeAssign(mFillGradient, fillGradient);
@@ -182,24 +201,28 @@
             float strokeScale, const SkMatrix& matrix) override;
 
 private:
+    enum class Property {
+        StrokeWidth = 0,
+        StrokeColor,
+        StrokeAlpha,
+        FillColor,
+        FillAlpha,
+        TrimPathStart,
+        TrimPathEnd,
+        TrimPathOffset,
+        StrokeLineCap,
+        StrokeLineJoin,
+        StrokeMiterLimit,
+        Count,
+    };
     // Applies trimming to the specified path.
     void applyTrim();
-    float mStrokeWidth = 0;
-    SkColor mStrokeColor = SK_ColorTRANSPARENT;
-    float mStrokeAlpha = 1;
-    SkColor mFillColor = SK_ColorTRANSPARENT;
-    SkShader* mStrokeGradient = nullptr;
-    SkShader* mFillGradient = nullptr;
-    float mFillAlpha = 1;
-    float mTrimPathStart = 0;
-    float mTrimPathEnd = 1;
-    float mTrimPathOffset = 0;
+    Properties mProperties;
     bool mTrimDirty = true;
-    SkPaint::Cap mStrokeLineCap = SkPaint::Cap::kButt_Cap;
-    SkPaint::Join mStrokeLineJoin = SkPaint::Join::kMiter_Join;
-    float mStrokeMiterLimit = 4;
     SkPath mTrimmedSkPath;
     SkPaint mPaint;
+    SkShader* mStrokeGradient = nullptr;
+    SkShader* mFillGradient = nullptr;
 };
 
 class ANDROID_API ClipPath: public Path {
@@ -216,49 +239,58 @@
 
 class ANDROID_API Group: public Node {
 public:
+    struct Properties {
+        float rotate = 0;
+        float pivotX = 0;
+        float pivotY = 0;
+        float scaleX = 1;
+        float scaleY = 1;
+        float translateX = 0;
+        float translateY = 0;
+    };
     Group(const Group& group);
     Group() {}
     float getRotation() {
-        return mRotate;
+        return mProperties.rotate;
     }
     void setRotation(float rotation) {
-        mRotate = rotation;
+        mProperties.rotate = rotation;
     }
     float getPivotX() {
-        return mPivotX;
+        return mProperties.pivotX;
     }
     void setPivotX(float pivotX) {
-        mPivotX = pivotX;
+        mProperties.pivotX = pivotX;
     }
     float getPivotY() {
-        return mPivotY;
+        return mProperties.pivotY;
     }
     void setPivotY(float pivotY) {
-        mPivotY = pivotY;
+        mProperties.pivotY = pivotY;
     }
     float getScaleX() {
-        return mScaleX;
+        return mProperties.scaleX;
     }
     void setScaleX(float scaleX) {
-        mScaleX = scaleX;
+        mProperties.scaleX = scaleX;
     }
     float getScaleY() {
-        return mScaleY;
+        return mProperties.scaleY;
     }
     void setScaleY(float scaleY) {
-        mScaleY = scaleY;
+        mProperties.scaleY = scaleY;
     }
     float getTranslateX() {
-        return mTranslateX;
+        return mProperties.translateX;
     }
     void setTranslateX(float translateX) {
-        mTranslateX = translateX;
+        mProperties.translateX = translateX;
     }
     float getTranslateY() {
-        return mTranslateY;
+        return mProperties.translateY;
     }
     void setTranslateY(float translateY) {
-        mTranslateY = translateY;
+        mProperties.translateY = translateY;
     }
     virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
             float scaleX, float scaleY) override;
@@ -268,38 +300,33 @@
     void addChild(Node* child);
     void dump() override;
     bool getProperties(float* outProperties, int length);
+    float getPropertyValue(int propertyId) const;
+    void setPropertyValue(int propertyId, float value);
+    static bool isValidProperty(int propertyId);
 
 private:
     enum class Property {
-        Rotate_Property = 0,
-        PivotX_Property,
-        PivotY_Property,
-        ScaleX_Property,
-        ScaleY_Property,
-        TranslateX_Property,
-        TranslateY_Property,
+        Rotate = 0,
+        PivotX,
+        PivotY,
+        ScaleX,
+        ScaleY,
+        TranslateX,
+        TranslateY,
         // Count of the properties, must be at the end.
         Count,
     };
-    float mRotate = 0;
-    float mPivotX = 0;
-    float mPivotY = 0;
-    float mScaleX = 1;
-    float mScaleY = 1;
-    float mTranslateX = 0;
-    float mTranslateY = 0;
-    std::vector<Node*> mChildren;
+    std::vector< std::unique_ptr<Node> > mChildren;
+    Properties mProperties;
 };
 
-class ANDROID_API Tree {
+class ANDROID_API Tree : public VirtualLightRefBase {
 public:
     Tree(Group* rootNode) : mRootNode(rootNode) {}
     void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
             const SkRect& bounds, bool needsMirroring, bool canReuseCache);
-    void drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
-            const SkRect& originalBounds);
 
-    void updateCachedBitmap(int width, int height);
+    const SkBitmap& getBitmapUpdateIfDirty();
     void createCachedBitmapIfNeeded(int width, int height);
     bool canReuseBitmap(int width, int height);
     void setAllowCaching(bool allowCaching) {
@@ -316,6 +343,10 @@
         mViewportWidth = viewportWidth;
         mViewportHeight = viewportHeight;
     }
+    SkPaint* getPaint();
+    const SkRect& getBounds() const {
+        return mBounds;
+    }
 
 private:
     // Cap the bitmap size, such that it won't hurt the performance too much
@@ -329,7 +360,7 @@
     float mViewportHeight = 0;
     float mRootAlpha = 1.0f;
 
-    Group* mRootNode;
+    std::unique_ptr<Group> mRootNode;
     SkRect mBounds;
     SkMatrix mCanvasMatrix;
     SkPaint mPaint;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 3007d86..b26b310 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1784,9 +1784,9 @@
      * Note that the actual playback of this data might occur after this function returns.
      *
      * @param audioData the array that holds the data to play.
-     * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+     * @param offsetInBytes the offset expressed in bytes in audioData where the data to write
      *    starts.
-     * @param sizeInBytes the number of bytes to read in audioData after the offset.
+     * @param sizeInBytes the number of bytes to write in audioData after the offset.
      * @return zero or the positive number of bytes that were written, or
      *    {@link #ERROR_INVALID_OPERATION}
      *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1821,9 +1821,9 @@
      * Note that the actual playback of this data might occur after this function returns.
      *
      * @param audioData the array that holds the data to play.
-     * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+     * @param offsetInBytes the offset expressed in bytes in audioData where the data to write
      *    starts.
-     * @param sizeInBytes the number of bytes to read in audioData after the offset.
+     * @param sizeInBytes the number of bytes to write in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1920,8 +1920,8 @@
      * In static buffer mode, copies the data to the buffer starting at offset 0.
      * Note that the actual playback of this data might occur after this function returns.
      *
-     * @param audioData the array that holds the data to play.
-     * @param offsetInShorts the offset expressed in shorts in audioData where the data to play
+     * @param audioData the array that holds the data to write.
+     * @param offsetInShorts the offset expressed in shorts in audioData where the data to write
      *     starts.
      * @param sizeInShorts the number of shorts to read in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
@@ -1987,7 +1987,7 @@
      * and the write mode is ignored.
      * Note that the actual playback of this data might occur after this function returns.
      *
-     * @param audioData the array that holds the data to play.
+     * @param audioData the array that holds the data to write.
      *     The implementation does not clip for sample values within the nominal range
      *     [-1.0f, 1.0f], provided that all gains in the audio pipeline are
      *     less than or equal to unity (1.0f), and in the absence of post-processing effects
@@ -1998,8 +1998,8 @@
      *     and later processing in the audio path.  Therefore applications are encouraged
      *     to provide samples values within the nominal range.
      * @param offsetInFloats the offset, expressed as a number of floats,
-     *     in audioData where the data to play starts.
-     * @param sizeInFloats the number of floats to read in audioData after the offset.
+     *     in audioData where the data to write starts.
+     * @param sizeInFloats the number of floats to write in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -2070,7 +2070,7 @@
      * and the write mode is ignored.
      * Note that the actual playback of this data might occur after this function returns.
      *
-     * @param audioData the buffer that holds the data to play, starting at the position reported
+     * @param audioData the buffer that holds the data to write, starting at the position reported
      *     by <code>audioData.position()</code>.
      *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
      *     have been advanced to reflect the amount of data that was successfully written to
@@ -2137,7 +2137,7 @@
     /**
      * Writes the audio data to the audio sink for playback in streaming mode on a HW_AV_SYNC track.
      * The blocking behavior will depend on the write mode.
-     * @param audioData the buffer that holds the data to play, starting at the position reported
+     * @param audioData the buffer that holds the data to write, starting at the position reported
      *     by <code>audioData.position()</code>.
      *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
      *     have been advanced to reflect the amount of data that was successfully written to
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index b791ef1..73571af 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -32,23 +32,6 @@
         android:imeOptions="actionSearch"
         android:visible="false" />
     <item
-        android:id="@+id/menu_sort"
-        android:title="@string/menu_sort"
-        android:icon="@drawable/ic_menu_sortby"
-        android:showAsAction="always">
-        <menu>
-            <item
-                android:id="@+id/menu_sort_name"
-                android:title="@string/sort_name" />
-            <item
-                android:id="@+id/menu_sort_date"
-                android:title="@string/sort_date" />
-            <item
-                android:id="@+id/menu_sort_size"
-                android:title="@string/sort_size" />
-        </menu>
-    </item>
-    <item
         android:id="@+id/menu_grid"
         android:title="@string/menu_grid"
         android:icon="@drawable/ic_menu_view_grid"
@@ -70,7 +53,7 @@
         android:title="@string/menu_create_dir"
         android:icon="@drawable/ic_menu_new_folder"
         android:alphabeticShortcut="e"
-        android:showAsAction="always"
+        android:showAsAction="never"
         android:visible="false" />
     <item
         android:id="@+id/menu_paste_from_clipboard"
@@ -80,6 +63,23 @@
         android:visible="false" />
     <!-- Copy action is defined in mode_directory.xml -->
     <item
+        android:id="@+id/menu_sort"
+        android:title="@string/menu_sort"
+        android:icon="@drawable/ic_menu_sortby"
+        android:showAsAction="never">
+        <menu>
+            <item
+                android:id="@+id/menu_sort_name"
+                android:title="@string/sort_name" />
+            <item
+                android:id="@+id/menu_sort_date"
+                android:title="@string/sort_date" />
+            <item
+                android:id="@+id/menu_sort_size"
+                android:title="@string/sort_size" />
+        </menu>
+    </item>
+    <item
         android:id="@+id/menu_file_size"
         android:showAsAction="never"
         android:visible="false" />
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 1e11b4e..3c21a21 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -65,6 +65,9 @@
 
     static final String EXTRA_STATE = "state";
 
+    // See comments where this const is referenced for details.
+    private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
+
     State mState;
     RootsCache mRoots;
     SearchManager mSearchManager;
@@ -72,9 +75,14 @@
     NavigationView mNavigator;
 
     private final String mTag;
+
     @LayoutRes
     private int mLayoutId;
 
+    // Track the time we opened the drawer in response to back being pressed.
+    // We use the time gap to figure out whether to close app or reopen the drawer.
+    private long mDrawerLastFiddled;
+
     public abstract void onDocumentPicked(DocumentInfo doc, @Nullable SiblingProvider siblings);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
 
@@ -232,21 +240,6 @@
         }
     }
 
-    void expandMenus(Menu menu) {
-        for (int i = 0; i < menu.size(); i++) {
-            final MenuItem item = menu.getItem(i);
-            switch (item.getItemId()) {
-                case R.id.menu_advanced:
-                case R.id.menu_file_size:
-                case R.id.menu_new_window:
-                case R.id.menu_search:
-                    break;
-                default:
-                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-            }
-        }
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
 
@@ -541,16 +534,35 @@
             return;
         }
 
-        final int size = mState.stack.size();
+        int size = mState.stack.size();
 
-        if (mDrawer.isOpen()) {
-            mDrawer.setOpen(false);
-        } else if (size > 1) {
+        // Do some "do what a I want" drawer fiddling, but don't
+        // do it if user already hit back recently and we recently
+        // did some fiddling.
+        if ((System.currentTimeMillis() - mDrawerLastFiddled) > DRAWER_NO_FIDDLE_DELAY) {
+            // Close drawer if it is open.
+            if (mDrawer.isOpen()) {
+                mDrawer.setOpen(false);
+                mDrawerLastFiddled = System.currentTimeMillis();
+                return;
+            }
+
+            // Open the Close drawer if it is closed and we're at the top of a root.
+            if (size == 1) {
+                mDrawer.setOpen(true);
+                // Remember so we don't just close it again if back is pressed again.
+                mDrawerLastFiddled = System.currentTimeMillis();
+                return;
+            }
+        }
+
+        if (size > 1) {
             mState.stack.pop();
             refreshCurrentRootAndDirectory(ANIM_LEAVE);
-        } else {
-            super.onBackPressed();
+            return;
         }
+
+        super.onBackPressed();
     }
 
     public void onStackPicked(DocumentStack stack) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 5a092db..3485fe4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -221,14 +221,6 @@
     }
 
     @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        boolean showMenu = super.onCreateOptionsMenu(menu);
-
-        expandMenus(menu);
-        return showMenu;
-    }
-
-    @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
index dae4bf7..d589d5e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
@@ -100,14 +100,12 @@
 
         final MenuItem advanced = menu.findItem(R.id.menu_advanced);
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
 
         advanced.setVisible(false);
         createDir.setVisible(false);
         pasteFromCb.setEnabled(false);
-        newWindow.setEnabled(false);
         fileSize.setVisible(false);
 
         Menus.disableHiddenItems(menu);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index c55f814..c81f342 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -185,14 +185,6 @@
     }
 
     @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        boolean showMenu = super.onCreateOptionsMenu(menu);
-
-        expandMenus(menu);
-        return showMenu;
-    }
-
-    @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
@@ -201,15 +193,13 @@
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
+        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
 
         createDir.setVisible(true);
         createDir.setEnabled(canCreateDirectory());
         pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
         settings.setVisible(root.hasSettings());
-
-        // TODO: For some reason settings menu item is not
-        // honoring the "showAsAction=never" setting in activity.xml.
-        settings.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        newWindow.setVisible(true);
 
         Menus.disableHiddenItems(menu, pasteFromCb);
         return true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index f5a2aae..d368de9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -15,40 +15,19 @@
  */
 package com.android.settingslib;
 
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.SystemProperties;
-import android.os.UserManager;
-import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 
 public class TetherUtil {
 
-    // Extras used for communicating with the TetherService.
-    public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
-    public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
-    public static final String EXTRA_SET_ALARM = "extraSetAlarm";
-    /**
-     * Tells the service to run a provision check now.
-     */
-    public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-
     public static boolean setWifiTethering(boolean enable, Context context) {
         final WifiManager wifiManager =
                 (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         return wifiManager.setWifiApEnabled(null, enable);
     }
 
-    public static boolean isWifiTetherEnabled(Context context) {
-        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        return wifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED;
-    }
-
     private static boolean isEntitlementCheckRequired(Context context) {
         final CarrierConfigManager configManager = (CarrierConfigManager) context
              .getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -71,13 +50,4 @@
         }
         return (provisionApp.length == 2);
     }
-
-    public static boolean isTetheringSupported(Context context) {
-        final ConnectivityManager cm =
-                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        final boolean isAdminUser =
-                UserManager.get(context).isUserAdmin(ActivityManager.getCurrentUser());
-        return isAdminUser && cm.isTetheringSupported();
-    }
-
 }
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 083707a..a6ba8b5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -103,7 +103,7 @@
 
     <!-- The default tiles to display in QuickSettings -->
     <string name="quick_settings_tiles_default" translatable="false">
-        wifi,bt,flashlight,dnd,cell,battery,rotation,airplane,location,cast,work
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location
     </string>
 
     <!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index e770b5d..454d1ce 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -182,6 +182,7 @@
         mListening = true;
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
+        updateShowPercent();
         if (mDemoMode) return;
         mBatteryController.addStateChangedCallback(this);
     }
@@ -193,6 +194,11 @@
         mBatteryController.removeStateChangedCallback(this);
     }
 
+    public void disableShowPercent() {
+        mShowPercent = false;
+        postInvalidate();
+    }
+
     private void postInvalidate() {
         mHandler.post(new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 6840e08..29f8af2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -42,6 +42,7 @@
     private static final String TAG = "TileQueryHelper";
 
     private final ArrayList<TileInfo> mTiles = new ArrayList<>();
+    private final ArrayList<String> mSpecs = new ArrayList<>();
     private final Context mContext;
     private TileStateListener mListener;
 
@@ -76,7 +77,7 @@
                     mainHandler.post(new Runnable() {
                         @Override
                         public void run() {
-                            addTile(spec, state, mTiles);
+                            addTile(spec, state);
                             mListener.onTilesChanged(mTiles);
                         }
                     });
@@ -95,20 +96,23 @@
         mListener = listener;
     }
 
-    private static void addTile(String spec, QSTile.State state, List<TileInfo> tiles) {
+    private void addTile(String spec, QSTile.State state) {
+        if (mSpecs.contains(spec)) {
+            return;
+        }
         TileInfo info = new TileInfo();
         info.state = state;
         info.spec = spec;
-        tiles.add(info);
+        mTiles.add(info);
+        mSpecs.add(spec);
     }
 
-    private static void addTile(String spec, Drawable drawable, CharSequence label, Context context,
-            List<TileInfo> tiles) {
+    private void addTile(String spec, Drawable drawable, CharSequence label, Context context) {
         QSTile.State state = new QSTile.State();
         state.label = label;
         state.contentDescription = label;
         state.icon = new DrawableIcon(drawable);
-        addTile(spec, state, tiles);
+        addTile(spec, state);
     }
 
     public static class TileInfo {
@@ -133,7 +137,7 @@
                     icon.setTint(mContext.getColor(android.R.color.white));
                 }
                 CharSequence label = info.serviceInfo.loadLabel(pm);
-                addTile(spec, icon, label != null ? label.toString() : "null", mContext, tiles);
+                addTile(spec, icon, label != null ? label.toString() : "null", mContext);
             }
             return tiles;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index cd3e3b2..72cdf18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -153,6 +153,7 @@
         private void bindView() {
             mDrawable.onBatteryLevelChanged(100, false, false);
             mDrawable.onPowerSaveChanged(true);
+            mDrawable.disableShowPercent();
             ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
             Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
             checkbox.setChecked(mPowerSave);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 9359301..d625fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -27,7 +27,6 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.Space;
-
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.tuner.TunerService;
@@ -58,8 +57,9 @@
     public static final String KEY_IMAGE_DELIM = ":";
     public static final String KEY_CODE_END = ")";
 
-    protected final LayoutInflater mLayoutInflater;
-    protected final LayoutInflater mLandscapeInflater;
+    protected LayoutInflater mLayoutInflater;
+    protected LayoutInflater mLandscapeInflater;
+    private int mDensity;
 
     protected FrameLayout mRot0;
     protected FrameLayout mRot90;
@@ -69,11 +69,27 @@
 
     public NavigationBarInflaterView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLayoutInflater = LayoutInflater.from(context);
+        mDensity = context.getResources().getConfiguration().densityDpi;
+        createInflaters();
+    }
+
+    private void createInflaters() {
+        mLayoutInflater = LayoutInflater.from(mContext);
         Configuration landscape = new Configuration();
-        landscape.setTo(context.getResources().getConfiguration());
+        landscape.setTo(mContext.getResources().getConfiguration());
         landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;
-        mLandscapeInflater = LayoutInflater.from(context.createConfigurationContext(landscape));
+        mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mDensity != newConfig.densityDpi) {
+            mDensity = newConfig.densityDpi;
+            createInflaters();
+            clearViews();
+            inflateLayout(mCurrentLayout);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 9996b75..45aae2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -30,12 +30,12 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.FontSizeUtils;
@@ -226,12 +226,25 @@
 
     public void setExternalIcon(String slot) {
         int viewIndex = getViewIndex(getSlotIndex(slot));
+        int height = mContext.getResources().getDimensionPixelSize(
+                R.dimen.status_bar_icon_drawing_size);
         ImageView imageView = (ImageView) mStatusIcons.getChildAt(viewIndex);
         imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
         imageView.setAdjustViewBounds(true);
+        setHeightAndCenter(imageView, height);
         imageView = (ImageView) mStatusIconsKeyguard.getChildAt(viewIndex);
         imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
         imageView.setAdjustViewBounds(true);
+        setHeightAndCenter(imageView, height);
+    }
+
+    private void setHeightAndCenter(ImageView imageView, int height) {
+        ViewGroup.LayoutParams params = imageView.getLayoutParams();
+        params.height = height;
+        if (params instanceof LinearLayout.LayoutParams) {
+            ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;
+        }
+        imageView.setLayoutParams(params);
     }
 
     public void setIcon(String slot, StatusBarIcon icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index b036936..500d603 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -20,7 +20,6 @@
     void addCallback(Callback callback);
     void removeCallback(Callback callback);
     boolean isHotspotEnabled();
-    boolean isHotspotSupported();
     void setHotspotEnabled(boolean enabled);
     boolean isTetheringAllowed();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 61d26c7..07b7409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -27,8 +27,6 @@
 import android.os.UserManager;
 import android.util.Log;
 
-import com.android.settingslib.TetherUtil;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -98,11 +96,6 @@
     }
 
     @Override
-    public boolean isHotspotSupported() {
-        return TetherUtil.isTetheringSupported(mContext);
-    }
-
-    @Override
     public boolean isTetheringAllowed() {
         return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING,
                 UserHandle.of(mCurrentUser));
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3fd8b40..4300920 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2670,18 +2670,21 @@
     // if ro.tether.denied = true we default to no tethering
     // gservices could set the secure setting to 1 though to enable it on a build where it
     // had previously been turned off.
+    @Override
     public boolean isTetheringSupported() {
         enforceTetherAccessPermission();
         int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
         boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
                 && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-        return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 ||
+        return tetherEnabledInSettings && mUserManager.isAdminUser() &&
+                ((mTethering.getTetherableUsbRegexs().length != 0 ||
                 mTethering.getTetherableWifiRegexs().length != 0 ||
                 mTethering.getTetherableBluetoothRegexs().length != 0) &&
                 mTethering.getUpstreamIfaceTypes().length != 0);
     }
 
+    @Override
     public void startTethering(int type, ResultReceiver receiver,
             boolean showProvisioningUi) {
         ConnectivityManager.enforceTetherChangePermission(mContext);
@@ -2692,6 +2695,7 @@
         mTethering.startTethering(type, receiver, showProvisioningUi);
     }
 
+    @Override
     public void stopTethering(int type) {
         ConnectivityManager.enforceTetherChangePermission(mContext);
         mTethering.stopTethering(type);
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 3ce4452..5120e1b 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2800,14 +2800,13 @@
     }
 
     @Override
-    public void prepareUserStorage(
-            String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) {
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
         try {
             mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
-                    userId, serialNumber, ephemeral ? 1 : 0);
+                    userId, serialNumber, flags);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d646696..c021f4c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8770,6 +8770,13 @@
                         continue;
                     }
 
+                    if (!tr.mUserSetupComplete) {
+                        // Don't include task launched while user is not done setting-up.
+                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                "Skipping, user setup not complete: " + tr);
+                        continue;
+                    }
+
                     ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
                     if (!detailed) {
                         rti.baseIntent.replaceExtras((Bundle)null);
@@ -12425,9 +12432,8 @@
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
 
-            // Make sure we have the current profile info, since it is needed for
-            // security checks.
-            mUserController.updateCurrentProfileIdsLocked();
+            // Make sure we have the current profile info, since it is needed for security checks.
+            mUserController.onSystemReady();
 
             mRecentTasks.onSystemReady();
             // Check to see if there are any update receivers to run.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 33112c6..0b2ff65 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2601,9 +2601,11 @@
 
         stack.setVisibleBehindActivity(visible ? r : null);
         if (!visible) {
-            // Make the activity immediately above r opaque.
+            // If there is a translucent home activity, we need to force it stop being translucent,
+            // because we can't depend on the application to necessarily perform that operation.
+            // Check out b/14469711 for details.
             final ActivityRecord next = stack.findNextTranslucentActivity(r);
-            if (next != null) {
+            if (next != null && next.isHomeActivity()) {
                 mService.convertFromTranslucent(next.appToken);
             }
         }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 703dfca..10f0977 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -108,6 +108,7 @@
     private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents";
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
     private static final String ATTR_USERID = "user_id";
+    private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete";
     private static final String ATTR_EFFECTIVE_UID = "effective_uid";
     private static final String ATTR_TASKTYPE = "task_type";
     private static final String ATTR_FIRSTACTIVETIME = "first_active_time";
@@ -157,6 +158,8 @@
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
+    boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
+                                // was changed.
 
     int numFullscreen;      // Number of fullscreen activities.
 
@@ -318,7 +321,8 @@
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
             TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean privileged, boolean _realActivitySuspended) {
+            int resizeMode, boolean privileged, boolean _realActivitySuspended,
+            boolean userSetupComplete) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -341,6 +345,7 @@
         taskType = _taskType;
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = _userId;
+        mUserSetupComplete = userSetupComplete;
         effectiveUid = _effectiveUid;
         firstActiveTime = _firstActiveTime;
         lastActiveTime = _lastActiveTime;
@@ -441,6 +446,7 @@
         }
 
         userId = UserHandle.getUserId(info.applicationInfo.uid);
+        mUserSetupComplete = mService.mUserController.isUserSetupCompleteLocked(userId);
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
@@ -1071,6 +1077,7 @@
         out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
         out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
         out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
         out.attribute(null, ATTR_FIRSTACTIVETIME, String.valueOf(firstActiveTime));
@@ -1140,6 +1147,7 @@
         boolean askedCompatMode = false;
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
         int userId = 0;
+        boolean userSetupComplete = true;
         int effectiveUid = -1;
         String lastDescription = null;
         long firstActiveTime = -1;
@@ -1186,6 +1194,8 @@
                 askedCompatMode = Boolean.valueOf(attrValue);
             } else if (ATTR_USERID.equals(attrName)) {
                 userId = Integer.valueOf(attrValue);
+            } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
+                userSetupComplete = Boolean.valueOf(attrValue);
             } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
                 effectiveUid = Integer.valueOf(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
@@ -1285,7 +1295,7 @@
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
                 taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged,
-                realActivitySuspended);
+                realActivitySuspended, userSetupComplete);
         task.updateOverrideConfiguration(bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 551f332..2f63b2d3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -45,11 +46,14 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
@@ -66,10 +70,12 @@
 import android.os.UserManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.R;
@@ -145,6 +151,34 @@
 
     private final LockPatternUtils mLockPatternUtils;
 
+    // Set of users who have completed the set-up process.
+    private final SparseBooleanArray mSetupCompletedUsers = new SparseBooleanArray();
+    private final UserSetupCompleteContentObserver mUserSetupCompleteContentObserver;
+
+    private class UserSetupCompleteContentObserver extends ContentObserver {
+        private final Uri mUserSetupComplete = Settings.Secure.getUriFor(USER_SETUP_COMPLETE);
+
+        public UserSetupCompleteContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        void register(ContentResolver resolver) {
+            resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+            synchronized (mService) {
+                updateCurrentUserSetupCompleteLocked();
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mUserSetupComplete.equals(uri)) {
+                synchronized (mService) {
+                    updateCurrentUserSetupCompleteLocked();
+                }
+            }
+        }
+    }
+
     UserController(ActivityManagerService service) {
         mService = service;
         mHandler = mService.mHandler;
@@ -154,6 +188,7 @@
         mUserLru.add(UserHandle.USER_SYSTEM);
         mLockPatternUtils = new LockPatternUtils(mService.mContext);
         updateStartedUserArrayLocked();
+        mUserSetupCompleteContentObserver = new UserSetupCompleteContentObserver(mHandler);
     }
 
     void finishUserSwitch(UserState uss) {
@@ -424,6 +459,7 @@
                 mStartedUsers.remove(userId);
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLocked();
+                mSetupCompletedUsers.delete(userId);
 
                 mService.onUserStoppedLocked(userId);
                 // Clean up all state and processes associated with the user.
@@ -619,6 +655,7 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
+                updateCurrentUserSetupCompleteLocked();
 
                 if (foreground) {
                     mCurrentUserId = userId;
@@ -833,6 +870,17 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    void updateCurrentUserSetupCompleteLocked() {
+        final ContentResolver cr = mService.mContext.getContentResolver();
+        final boolean setupComplete =
+                Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, mCurrentUserId) != 0;
+        mSetupCompletedUsers.put(mCurrentUserId, setupComplete);
+    }
+
+    boolean isUserSetupCompleteLocked(int userId) {
+        return mSetupCompletedUsers.get(userId);
+    }
+
     private void stopBackgroundUsersIfEnforced(int oldUserId) {
         // Never stop system user
         if (oldUserId == UserHandle.USER_SYSTEM) {
@@ -1141,12 +1189,17 @@
         }
     }
 
+    void onSystemReady() {
+        updateCurrentProfileIdsLocked();
+        mUserSetupCompleteContentObserver.register(mService.mContext.getContentResolver());
+    }
+
     /**
      * Refreshes the list of users related to the current user when either a
      * user switch happens or when a new related user is started in the
      * background.
      */
-    void updateCurrentProfileIdsLocked() {
+    private void updateCurrentProfileIdsLocked() {
         final List<UserInfo> profiles = getUserManager().getProfiles(mCurrentUserId,
                 false /* enabledOnly */);
         int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a73a67a..760b218 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -451,7 +451,7 @@
                 ((BluetoothPan) proxy).setBluetoothTethering(enable);
                 // TODO: Enabling bluetooth tethering can fail asynchronously here.
                 // We should figure out a way to bubble up that failure instead of sending success.
-                int result = ((BluetoothPan) proxy).isTetheringOn() ?
+                int result = ((BluetoothPan) proxy).isTetheringOn() == enable ?
                         ConnectivityManager.TETHER_ERROR_NO_ERROR :
                         ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
                 sendTetherResult(receiver, result);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index f5da52e..29d52c1 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -250,6 +250,12 @@
         rebindServices();
     }
 
+    public void onUserUnlocked(int user) {
+        if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+        rebuildRestoredPackages();
+        rebindServices();
+    }
+
     public ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
         if (service == null) {
             return null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 078094c..bcb2c59 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -816,6 +816,12 @@
             } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 mZenModeHelper.onUserRemoved(user);
+            } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
+                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                mConditionProviders.onUserUnlocked(user);
+                mListeners.onUserUnlocked(user);
+                mAssistant.onUserUnlocked(user);
+                mZenModeHelper.onUserUnlocked(user);
             }
         }
     };
@@ -994,6 +1000,7 @@
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
         getContext().registerReceiver(mIntentReceiver, filter);
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b7abce21..7518c6e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -16,7 +16,6 @@
 
 package com.android.server.notification;
 
-import static android.media.AudioAttributes.USAGE_ALARM;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.media.AudioAttributes.USAGE_UNKNOWN;
@@ -195,19 +194,7 @@
     }
 
     public void onUserSwitched(int user) {
-        if (mUser == user || user < UserHandle.USER_SYSTEM) return;
-        mUser = user;
-        if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user);
-        ZenModeConfig config = mConfigs.get(user);
-        if (config == null) {
-            if (DEBUG) Log.d(TAG, "onUserSwitched: generating default config for user " + user);
-            config = mDefaultConfig.copy();
-            config.user = user;
-        }
-        synchronized (mConfig) {
-            setConfigLocked(config, "onUserSwitched");
-        }
-        cleanUpZenRules();
+        loadConfigForUser(user, "onUserSwitched");
     }
 
     public void onUserRemoved(int user) {
@@ -216,6 +203,26 @@
         mConfigs.remove(user);
     }
 
+    public void onUserUnlocked(int user) {
+        loadConfigForUser(user, "onUserUnlocked");
+    }
+
+    private void loadConfigForUser(int user, String reason) {
+        if (mUser == user || user < UserHandle.USER_SYSTEM) return;
+        mUser = user;
+        if (DEBUG) Log.d(TAG, reason + " u=" + user);
+        ZenModeConfig config = mConfigs.get(user);
+        if (config == null) {
+            if (DEBUG) Log.d(TAG, reason + " generating default config for user " + user);
+            config = mDefaultConfig.copy();
+            config.user = user;
+        }
+        synchronized (mConfig) {
+            setConfigLocked(config, reason);
+        }
+        cleanUpZenRules();
+    }
+
     public int getZenModeListenerInterruptionFilter() {
         return NotificationManager.zenModeToInterruptionFilter(mZenMode);
     }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index dac89ec..c08f713 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,11 +16,11 @@
 
 package com.android.server.pm;
 
-import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageStats;
 import android.os.Build;
+import android.os.storage.StorageManager;
 import android.util.Slog;
 
 import com.android.internal.os.InstallerConnection;
@@ -29,9 +29,6 @@
 
 import dalvik.system.VMRuntime;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
 
@@ -52,19 +49,9 @@
     /** This is an OTA update dexopt */
     public static final int DEXOPT_OTA          = 1 << 6;
 
-    /** @hide */
-    @IntDef(flag = true, value = {
-            FLAG_DE_STORAGE,
-            FLAG_CE_STORAGE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StorageFlags {}
-
-    public static final int FLAG_DE_STORAGE = 1 << 0;
-    public static final int FLAG_CE_STORAGE = 1 << 1;
-
-    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2;
-    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3;
+    // NOTE: keep in sync with installd
+    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
+    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
 
     private final InstallerConnection mInstaller;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 553a9a2..504ce31 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -235,7 +235,6 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
-import com.android.server.pm.Installer.StorageFlags;
 import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
@@ -2388,9 +2387,9 @@
             // can't wait for user to start
             final int flags;
             if (StorageManager.isFileBasedEncryptionEnabled()) {
-                flags = Installer.FLAG_DE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE;
             } else {
-                flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             }
             reconcileAppsData(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, flags);
 
@@ -6860,7 +6859,7 @@
 
     private boolean removeDataDirsLI(String volumeUuid, String packageName) {
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
 
         boolean res = true;
         final int[] users = sUserManager.getUserIds();
@@ -6906,7 +6905,7 @@
 
     private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
 
         final int[] users = sUserManager.getUserIds();
         for (int user : users) {
@@ -13947,7 +13946,8 @@
                 outInfo.removedUsers = new int[] {removeUser};
             }
             // TODO: triage flags as part of 26466827
-            final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            final int installerFlags = StorageManager.FLAG_STORAGE_CE
+                    | StorageManager.FLAG_STORAGE_DE;
             try {
                 mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
             } catch (InstallerException e) {
@@ -14127,7 +14127,7 @@
         // record of app. This helps users recover from UID mismatches without
         // resorting to a full data wipe.
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
             mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags);
         } catch (InstallerException e) {
@@ -14362,7 +14362,7 @@
             return false;
         }
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
             mInstaller.clearAppData(p.volumeUuid, packageName, userId,
                     flags | Installer.FLAG_CLEAR_CACHE_ONLY);
@@ -14463,7 +14463,7 @@
         }
 
         // TODO: triage flags as part of 26466827
-        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
             mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath,
                     libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
@@ -16787,16 +16787,20 @@
         }
 
         // Reconcile app data for all started/unlocked users
+        final StorageManager sm = mContext.getSystemService(StorageManager.class);
         final UserManager um = mContext.getSystemService(UserManager.class);
         for (UserInfo user : um.getUsers()) {
+            final int flags;
             if (um.isUserUnlocked(user.id)) {
-                reconcileAppsData(volumeUuid, user.id,
-                        Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE);
+                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (um.isUserRunning(user.id)) {
-                reconcileAppsData(volumeUuid, user.id, Installer.FLAG_DE_STORAGE);
+                flags = StorageManager.FLAG_STORAGE_DE;
             } else {
                 continue;
             }
+
+            sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
+            reconcileAppsData(volumeUuid, user.id, flags);
         }
 
         synchronized (mPackages) {
@@ -16905,20 +16909,6 @@
                 }
             }
         }
-
-        final StorageManager sm = mContext.getSystemService(StorageManager.class);
-        final UserManager um = mContext.getSystemService(UserManager.class);
-        for (UserInfo user : um.getUsers()) {
-            final File userDir = Environment.getDataUserDirectory(volumeUuid, user.id);
-            if (userDir.exists()) continue;
-
-            try {
-                sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, user.isEphemeral());
-                UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
-            } catch (IOException e) {
-                Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
-            }
-        }
     }
 
     private void assertPackageKnown(String volumeUuid, String packageName)
@@ -16988,7 +16978,7 @@
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps on all mounted volumes.
      */
-    void reconcileAppsData(int userId, @StorageFlags int flags) {
+    void reconcileAppsData(int userId, int flags) {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final String volumeUuid = vol.getFsUuid();
@@ -17005,7 +16995,7 @@
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps.
      */
-    private void reconcileAppsData(String volumeUuid, int userId, @StorageFlags int flags) {
+    private void reconcileAppsData(String volumeUuid, int userId, int flags) {
         Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
                 + Integer.toHexString(flags));
 
@@ -17016,7 +17006,7 @@
 
         // First look for stale data that doesn't belong, and check if things
         // have changed since we did our last restorecon
-        if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
             if (!isUserKeyUnlocked(userId)) {
                 throw new RuntimeException(
                         "Yikes, someone asked us to reconcile CE storage while " + userId
@@ -17034,12 +17024,12 @@
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
                     synchronized (mInstallLock) {
                         destroyAppDataLI(volumeUuid, packageName, userId,
-                                Installer.FLAG_CE_STORAGE);
+                                StorageManager.FLAG_STORAGE_CE);
                     }
                 }
             }
         }
-        if ((flags & Installer.FLAG_DE_STORAGE) != 0) {
+        if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
             restoreconNeeded |= SELinuxMMAC.isRestoreconNeeded(deDir);
 
             final File[] files = FileUtils.listFilesOrEmpty(deDir);
@@ -17051,7 +17041,7 @@
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
                     synchronized (mInstallLock) {
                         destroyAppDataLI(volumeUuid, packageName, userId,
-                                Installer.FLAG_DE_STORAGE);
+                                StorageManager.FLAG_STORAGE_DE);
                     }
                 }
             }
@@ -17080,10 +17070,10 @@
         }
 
         if (restoreconNeeded) {
-            if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
                 SELinuxMMAC.setRestoreconDone(ceDir);
             }
-            if ((flags & Installer.FLAG_DE_STORAGE) != 0) {
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
                 SELinuxMMAC.setRestoreconDone(deDir);
             }
         }
@@ -17112,9 +17102,9 @@
         for (UserInfo user : um.getUsers()) {
             final int flags;
             if (um.isUserUnlocked(user.id)) {
-                flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (um.isUserRunning(user.id)) {
-                flags = Installer.FLAG_DE_STORAGE;
+                flags = StorageManager.FLAG_STORAGE_DE;
             } else {
                 continue;
             }
@@ -17135,7 +17125,7 @@
      * will try recovering system apps by wiping data; third-party app data is
      * left intact.
      */
-    private void prepareAppData(String volumeUuid, int userId, @StorageFlags int flags,
+    private void prepareAppData(String volumeUuid, int userId, int flags,
             PackageParser.Package pkg, boolean restoreconNeeded) {
         if (DEBUG_APP_DATA) {
             Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
@@ -17173,7 +17163,7 @@
                 restoreconAppDataLI(volumeUuid, packageName, userId, flags, appId, app.seinfo);
             }
 
-            if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
                 // Create a native library symlink only if we have native libraries
                 // and if the native libraries are 32 bit libraries. We do not provide
                 // this symlink for 64 bit libraries.
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bbbe693..fcb777b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3787,7 +3787,7 @@
                 continue;
             }
             // TODO: triage flags!
-            final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
             try {
                 installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
                         seinfos[i], targetSdkVersions[i]);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5f46567..3cc7b10 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -25,7 +25,6 @@
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
-import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -78,7 +77,9 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
-import com.android.server.pm.Installer.StorageFlags;
+
+import libcore.io.IoUtils;
+import libcore.util.Objects;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -96,9 +97,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import libcore.io.IoUtils;
-import libcore.util.Objects;
-
 /**
  * Service for {@link UserManager}.
  *
@@ -1893,17 +1891,8 @@
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                try {
-                    final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
-                    storage.prepareUserStorage(
-                            volumeUuid, userId, userInfo.serialNumber, userInfo.isEphemeral());
-                    enforceSerialNumber(userDir, userInfo.serialNumber);
-                } catch (IOException e) {
-                    Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
-                }
-            }
+            prepareUserStorage(userId, userInfo.serialNumber,
+                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             mPm.createNewUser(userId);
             userInfo.partial = false;
             synchronized (mPackagesLock) {
@@ -2466,11 +2455,24 @@
     }
 
     /**
+     * Prepare storage areas for given user on all mounted devices.
+     */
+    private void prepareUserStorage(int userId, int userSerial, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+            final String volumeUuid = vol.getFsUuid();
+            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+        }
+    }
+
+    /**
      * Called right before a user is started. This gives us a chance to prepare
      * app storage and apply any user restrictions.
      */
     public void onBeforeStartUser(int userId) {
-        mPm.reconcileAppsData(userId, Installer.FLAG_DE_STORAGE);
+        final int userSerial = getUserSerialNumber(userId);
+        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
 
         if (userId != UserHandle.USER_SYSTEM) {
             synchronized (mRestrictionsLock) {
@@ -2484,7 +2486,9 @@
      * app storage.
      */
     public void onBeforeUnlockUser(int userId) {
-        mPm.reconcileAppsData(userId, Installer.FLAG_CE_STORAGE);
+        final int userSerial = getUserSerialNumber(userId);
+        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
     }
 
     /**
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 7d7be07..071ec1b0 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -15,6 +15,7 @@
     services.core \
     services.devicepolicy \
     services.net \
+    services.usage \
     easymocklib \
     guava \
     android-support-test \
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
new file mode 100644
index 0000000..9ccb1a6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import android.os.FileUtils;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+public class AppIdleHistoryTests extends AndroidTestCase {
+
+    File mStorageDir;
+
+    final static String PACKAGE_1 = "com.android.testpackage1";
+    final static String PACKAGE_2 = "com.android.testpackage2";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStorageDir = new File(getContext().getFilesDir(), "appidle");
+        mStorageDir.mkdirs();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        FileUtils.deleteContents(mStorageDir);
+        super.tearDown();
+    }
+
+    public void testFilesCreation() {
+        final int userId = 0;
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);
+
+        aih.updateDisplayLocked(true, /* elapsedRealtime= */ 1000);
+        aih.updateDisplayLocked(false, /* elapsedRealtime= */ 2000);
+        // Screen On time file should be written right away
+        assertTrue(aih.getScreenOnTimeFile().exists());
+
+        aih.writeAppIdleTimesLocked(userId);
+        // stats file should be written now
+        assertTrue(new File(new File(mStorageDir, "users/" + userId),
+                AppIdleHistory.APP_IDLE_FILENAME).exists());
+    }
+
+    public void testScreenOnTime() {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.updateDisplayLocked(false, 2000);
+        assertEquals(aih.getScreenOnTimeLocked(2000), 0);
+        aih.updateDisplayLocked(true, 3000);
+        assertEquals(aih.getScreenOnTimeLocked(4000), 1000);
+        assertEquals(aih.getScreenOnTimeLocked(5000), 2000);
+        aih.updateDisplayLocked(false, 6000);
+        // Screen on time should not keep progressing with screen is off
+        assertEquals(aih.getScreenOnTimeLocked(7000), 3000);
+        assertEquals(aih.getScreenOnTimeLocked(8000), 3000);
+        aih.writeElapsedTimeLocked();
+
+        // Check if the screen on time is persisted across instantiations
+        AppIdleHistory aih2 = new AppIdleHistory(mStorageDir, 0);
+        assertEquals(aih2.getScreenOnTimeLocked(11000), 3000);
+        aih2.updateDisplayLocked(true, 4000);
+        aih2.updateDisplayLocked(false, 5000);
+        assertEquals(aih2.getScreenOnTimeLocked(13000), 4000);
+    }
+
+    public void testPackageEvents() {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.setThresholds(4000, 1000);
+        aih.updateDisplayLocked(true, 1000);
+        // App is not-idle by default
+        assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 1500));
+        // Still not idle
+        assertFalse(aih.isIdleLocked(PACKAGE_1, 0, 3000));
+        // Idle now
+        assertTrue(aih.isIdleLocked(PACKAGE_1, 0, 8000));
+        // Not idle
+        assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 9000));
+
+        // Screen off
+        aih.updateDisplayLocked(false, 9100);
+        // Still idle after 10 seconds because screen hasn't been on long enough
+        assertFalse(aih.isIdleLocked(PACKAGE_2, 0, 20000));
+        aih.updateDisplayLocked(true, 21000);
+        assertTrue(aih.isIdleLocked(PACKAGE_2, 0, 23000));
+    }
+}
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index e3c0868..3e2b43d 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,19 +16,45 @@
 
 package com.android.server.usage;
 
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
 /**
  * Keeps track of recent active state changes in apps.
  * Access should be guarded by a lock by the caller.
  */
 public class AppIdleHistory {
 
-    private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>();
-    private long lastPeriod = 0;
+    private static final String TAG = "AppIdleHistory";
+
+    // History for all users and all packages
+    private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>();
+    private long mLastPeriod = 0;
     private static final long ONE_MINUTE = 60 * 1000;
     private static final int HISTORY_SIZE = 100;
     private static final int FLAG_LAST_STATE = 2;
@@ -36,77 +62,353 @@
     private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE
             : 60 * ONE_MINUTE;
 
-    public void addEntry(String packageName, int userId, boolean idle, long timeNow) {
-        ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
-        byte[] packageHistory = getPackageHistory(userHistory, packageName);
+    @VisibleForTesting
+    static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
+    private static final String TAG_PACKAGES = "packages";
+    private static final String TAG_PACKAGE = "package";
+    private static final String ATTR_NAME = "name";
+    // Screen on timebase time when app was last used
+    private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
+    // Elapsed timebase time when app was last used
+    private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
 
-        long thisPeriod = timeNow / PERIOD_DURATION;
+    // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
+    private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
+    private long mElapsedDuration; // Total device on duration since device was "born"
+
+    // screen on time = mScreenOnDuration + (timeNow - mScreenOnSnapshot)
+    private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
+    private long mScreenOnDuration; // Total screen on duration since device was "born"
+
+    private long mElapsedTimeThreshold;
+    private long mScreenOnTimeThreshold;
+    private final File mStorageDir;
+
+    private boolean mScreenOn;
+
+    private static class PackageHistory {
+        final byte[] recent = new byte[HISTORY_SIZE];
+        long lastUsedElapsedTime;
+        long lastUsedScreenTime;
+    }
+
+    AppIdleHistory(long elapsedRealtime) {
+        this(Environment.getDataSystemDirectory(), elapsedRealtime);
+    }
+
+    @VisibleForTesting
+    AppIdleHistory(File storageDir, long elapsedRealtime) {
+        mElapsedSnapshot = elapsedRealtime;
+        mScreenOnSnapshot = elapsedRealtime;
+        mStorageDir = storageDir;
+        readScreenOnTimeLocked();
+    }
+
+    public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
+        mElapsedTimeThreshold = elapsedTimeThreshold;
+        mScreenOnTimeThreshold = screenOnTimeThreshold;
+    }
+
+    public void updateDisplayLocked(boolean screenOn, long elapsedRealtime) {
+        if (screenOn == mScreenOn) return;
+
+        mScreenOn = screenOn;
+        if (mScreenOn) {
+            mScreenOnSnapshot = elapsedRealtime;
+        } else {
+            mScreenOnDuration += elapsedRealtime - mScreenOnSnapshot;
+            mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
+            writeScreenOnTimeLocked();
+            mElapsedSnapshot = elapsedRealtime;
+        }
+    }
+
+    public long getScreenOnTimeLocked(long elapsedRealtime) {
+        long screenOnTime = mScreenOnDuration;
+        if (mScreenOn) {
+            screenOnTime += elapsedRealtime - mScreenOnSnapshot;
+        }
+        return screenOnTime;
+    }
+
+    @VisibleForTesting
+    File getScreenOnTimeFile() {
+        return new File(mStorageDir, "screen_on_time");
+    }
+
+    private void readScreenOnTimeLocked() {
+        File screenOnTimeFile = getScreenOnTimeFile();
+        if (screenOnTimeFile.exists()) {
+            try {
+                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
+                mScreenOnDuration = Long.parseLong(reader.readLine());
+                mElapsedDuration = Long.parseLong(reader.readLine());
+                reader.close();
+            } catch (IOException | NumberFormatException e) {
+            }
+        } else {
+            writeScreenOnTimeLocked();
+        }
+    }
+
+    private void writeScreenOnTimeLocked() {
+        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
+        FileOutputStream fos = null;
+        try {
+            fos = screenOnTimeFile.startWrite();
+            fos.write((Long.toString(mScreenOnDuration) + "\n"
+                    + Long.toString(mElapsedDuration) + "\n").getBytes());
+            screenOnTimeFile.finishWrite(fos);
+        } catch (IOException ioe) {
+            screenOnTimeFile.failWrite(fos);
+        }
+    }
+
+    /**
+     * To be called periodically to keep track of elapsed time when app idle times are written
+     */
+    public void writeElapsedTimeLocked() {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        // Only bump up and snapshot the elapsed time. Don't change screen on duration.
+        mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
+        mElapsedSnapshot = elapsedRealtime;
+        writeScreenOnTimeLocked();
+    }
+
+    public void reportUsageLocked(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+
+        shiftHistoryToNow(userHistory, elapsedRealtime);
+
+        packageHistory.lastUsedElapsedTime = mElapsedDuration
+                + (elapsedRealtime - mElapsedSnapshot);
+        packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
+        packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
+    }
+
+    public void setIdle(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+
+        shiftHistoryToNow(userHistory, elapsedRealtime);
+
+        packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
+    }
+
+    private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory,
+            long elapsedRealtime) {
+        long thisPeriod = elapsedRealtime / PERIOD_DURATION;
         // Has the period switched over? Slide all users' package histories
-        if (lastPeriod != 0 && lastPeriod < thisPeriod
-                && (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) {
-            int diff = (int) (thisPeriod - lastPeriod);
+        if (mLastPeriod != 0 && mLastPeriod < thisPeriod
+                && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) {
+            int diff = (int) (thisPeriod - mLastPeriod);
             final int NUSERS = mIdleHistory.size();
             for (int u = 0; u < NUSERS; u++) {
                 userHistory = mIdleHistory.valueAt(u);
-                for (byte[] history : userHistory.values()) {
+                for (PackageHistory idleState : userHistory.values()) {
                     // Shift left
-                    System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff);
+                    System.arraycopy(idleState.recent, diff, idleState.recent, 0,
+                            HISTORY_SIZE - diff);
                     // Replicate last state across the diff
                     for (int i = 0; i < diff; i++) {
-                        history[HISTORY_SIZE - i - 1] =
-                                (byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
+                        idleState.recent[HISTORY_SIZE - i - 1] =
+                            (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
                     }
                 }
             }
         }
-        lastPeriod = thisPeriod;
-        if (!idle) {
-            packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        } else {
-            packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
-        }
+        mLastPeriod = thisPeriod;
     }
 
-    private ArrayMap<String, byte[]> getUserHistory(int userId) {
-        ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
+    private ArrayMap<String, PackageHistory> getUserHistoryLocked(int userId) {
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
         if (userHistory == null) {
             userHistory = new ArrayMap<>();
             mIdleHistory.put(userId, userHistory);
+            readAppIdleTimesLocked(userId, userHistory);
         }
         return userHistory;
     }
 
-    private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) {
-        byte[] packageHistory = userHistory.get(packageName);
+    private PackageHistory getPackageHistoryLocked(ArrayMap<String, PackageHistory> userHistory,
+            String packageName, long elapsedRealtime) {
+        PackageHistory packageHistory = userHistory.get(packageName);
         if (packageHistory == null) {
-            packageHistory = new byte[HISTORY_SIZE];
+            packageHistory = new PackageHistory();
+            packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime);
+            packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime);
             userHistory.put(packageName, packageHistory);
         }
         return packageHistory;
     }
 
-    public void removeUser(int userId) {
+    public void onUserRemoved(int userId) {
         mIdleHistory.remove(userId);
     }
 
-    public boolean isIdle(int userId, String packageName) {
-        ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
-        byte[] packageHistory = getPackageHistory(userHistory, packageName);
-        return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0;
+    public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory =
+                getPackageHistoryLocked(userHistory, packageName, elapsedRealtime);
+        if (packageHistory == null) {
+            return false; // Default to not idle
+        } else {
+            return hasPassedThresholdsLocked(packageHistory, elapsedRealtime);
+        }
+    }
+
+    private long getElapsedTimeLocked(long elapsedRealtime) {
+        return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
+    }
+
+    public void setIdleLocked(String packageName, int userId, boolean idle, long elapsedRealtime) {
+        ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+        PackageHistory packageHistory = getPackageHistoryLocked(userHistory, packageName,
+                elapsedRealtime);
+        packageHistory.lastUsedElapsedTime = getElapsedTimeLocked(elapsedRealtime)
+                - mElapsedTimeThreshold;
+        packageHistory.lastUsedScreenTime = getScreenOnTimeLocked(elapsedRealtime)
+                - (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
+    }
+
+    private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
+        return (packageHistory.lastUsedScreenTime
+                    <= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
+                && (packageHistory.lastUsedElapsedTime
+                        <= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold);
+    }
+
+    private File getUserFile(int userId) {
+        return new File(new File(new File(mStorageDir, "users"),
+                Integer.toString(userId)), APP_IDLE_FILENAME);
+    }
+
+    private void readAppIdleTimesLocked(int userId, ArrayMap<String, PackageHistory> userHistory) {
+        FileInputStream fis = null;
+        try {
+            AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
+            fis = appIdleFile.openRead();
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                // Skip
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                Slog.e(TAG, "Unable to read app idle file for user " + userId);
+                return;
+            }
+            if (!parser.getName().equals(TAG_PACKAGES)) {
+                return;
+            }
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG) {
+                    final String name = parser.getName();
+                    if (name.equals(TAG_PACKAGE)) {
+                        final String packageName = parser.getAttributeValue(null, ATTR_NAME);
+                        PackageHistory packageHistory = new PackageHistory();
+                        packageHistory.lastUsedElapsedTime =
+                                Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
+                        packageHistory.lastUsedScreenTime =
+                                Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
+                        userHistory.put(packageName, packageHistory);
+                    }
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Unable to read app idle file for user " + userId);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    public void writeAppIdleTimesLocked(int userId) {
+        FileOutputStream fos = null;
+        AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
+        try {
+            fos = appIdleFile.startWrite();
+            final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+            FastXmlSerializer xml = new FastXmlSerializer();
+            xml.setOutput(bos, StandardCharsets.UTF_8.name());
+            xml.startDocument(null, true);
+            xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            xml.startTag(null, TAG_PACKAGES);
+
+            ArrayMap<String,PackageHistory> userHistory = getUserHistoryLocked(userId);
+            final int N = userHistory.size();
+            for (int i = 0; i < N; i++) {
+                String packageName = userHistory.keyAt(i);
+                PackageHistory history = userHistory.valueAt(i);
+                xml.startTag(null, TAG_PACKAGE);
+                xml.attribute(null, ATTR_NAME, packageName);
+                xml.attribute(null, ATTR_ELAPSED_IDLE,
+                        Long.toString(history.lastUsedElapsedTime));
+                xml.attribute(null, ATTR_SCREEN_IDLE,
+                        Long.toString(history.lastUsedScreenTime));
+                xml.endTag(null, TAG_PACKAGE);
+            }
+
+            xml.endTag(null, TAG_PACKAGES);
+            xml.endDocument();
+            appIdleFile.finishWrite(fos);
+        } catch (Exception e) {
+            appIdleFile.failWrite(fos);
+            Slog.e(TAG, "Error writing app idle file for user " + userId);
+        }
     }
 
     public void dump(IndentingPrintWriter idpw, int userId) {
-        ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
+        idpw.println("Package idle stats:");
+        idpw.increaseIndent();
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long totalElapsedTime = getElapsedTimeLocked(elapsedRealtime);
+        final long screenOnTime = getScreenOnTimeLocked(elapsedRealtime);
         if (userHistory == null) return;
         final int P = userHistory.size();
         for (int p = 0; p < P; p++) {
             final String packageName = userHistory.keyAt(p);
-            final byte[] history = userHistory.valueAt(p);
+            final PackageHistory packageHistory = userHistory.valueAt(p);
+            idpw.print("package=" + packageName);
+            idpw.print(" lastUsedElapsed=");
+            TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw);
+            idpw.print(" lastUsedScreenOn=");
+            TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw);
+            idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
+            idpw.println();
+        }
+        idpw.println();
+        idpw.print("totalElapsedTime=");
+        TimeUtils.formatDuration(getElapsedTimeLocked(elapsedRealtime), idpw);
+        idpw.println();
+        idpw.print("totalScreenOnTime=");
+        TimeUtils.formatDuration(getScreenOnTimeLocked(elapsedRealtime), idpw);
+        idpw.println();
+        idpw.decreaseIndent();
+    }
+
+    public void dumpHistory(IndentingPrintWriter idpw, int userId) {
+        ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        if (userHistory == null) return;
+        final int P = userHistory.size();
+        for (int p = 0; p < P; p++) {
+            final String packageName = userHistory.keyAt(p);
+            final byte[] history = userHistory.valueAt(p).recent;
             for (int i = 0; i < HISTORY_SIZE; i++) {
                 idpw.print(history[i] == 0 ? '.' : 'A');
             }
+            idpw.print(" idle=" + (isIdleLocked(packageName, userId, elapsedRealtime) ? "y" : "n"));
             idpw.print("  " + packageName);
             idpw.println();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 7f379fe..f541f70 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -48,7 +48,6 @@
             usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
-            usageStats.mBeginIdleTime = 0;
             packageStats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
@@ -113,7 +112,6 @@
         if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
             usageStats.mLastTimeUsed = timeStamp;
         }
-        usageStats.mLastTimeSystemUsed = timeStamp;
         usageStats.mEndTimeStamp = timeStamp;
 
         if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
@@ -123,22 +121,6 @@
         endTime = timeStamp;
     }
 
-    /**
-     * Updates the last active time for the package. The timestamp uses a timebase that
-     * tracks the device usage time.
-     * @param packageName
-     * @param timeStamp
-     */
-    void updateBeginIdleTime(String packageName, long timeStamp) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.mBeginIdleTime = timeStamp;
-    }
-
-    void updateSystemLastUsedTime(String packageName, long lastUsedTime) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.mLastTimeSystemUsed = lastUsedTime;
-    }
-
     void updateConfigurationStats(Configuration config, long timeStamp) {
         if (activeConfiguration != null) {
             ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 77740387..46ad8a1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -40,6 +40,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.display.DisplayManager;
@@ -62,7 +63,6 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -77,12 +77,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.SystemService;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -106,7 +102,7 @@
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
     private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
 
-    long mAppIdleDurationMillis;
+    long mAppIdleScreenThresholdMillis;
     long mCheckIdleIntervalMillis;
     long mAppIdleWallclockThresholdMillis;
     long mAppIdleParoleIntervalMillis;
@@ -147,11 +143,8 @@
 
     private volatile boolean mPendingOneTimeCheckIdleStates;
 
-    long mScreenOnTime;
-    long mLastScreenOnEventRealtime;
-
     @GuardedBy("mLock")
-    private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
+    private AppIdleHistory mAppIdleHistory;
 
     private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
@@ -191,8 +184,7 @@
 
         synchronized (mLock) {
             cleanUpRemovedUsersLocked();
-            mLastScreenOnEventRealtime = SystemClock.elapsedRealtime();
-            mScreenOnTime = readScreenOnTimeLocked();
+            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
         }
 
         mRealTimeSnapshot = SystemClock.elapsedRealtime();
@@ -221,7 +213,7 @@
 
             mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
             synchronized (mLock) {
-                updateDisplayLocked();
+                mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime());
             }
 
             if (mPendingOneTimeCheckIdleStates) {
@@ -232,6 +224,11 @@
         }
     }
 
+    private boolean isDisplayOn() {
+        return mDisplayManager
+                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+    }
+
     private class UserActionsReceiver extends BroadcastReceiver {
 
         @Override
@@ -274,7 +271,8 @@
         @Override public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
                 synchronized (UsageStatsService.this.mLock) {
-                    updateDisplayLocked();
+                    mAppIdleHistory.updateDisplayLocked(isDisplayOn(),
+                            SystemClock.elapsedRealtime());
                 }
             }
         }
@@ -291,8 +289,25 @@
     }
 
     @Override
-    public long getAppIdleRollingWindowDurationMillis() {
-        return mAppIdleWallclockThresholdMillis * 2;
+    public void onNewUpdate(int userId) {
+        initializeDefaultsForSystemApps(userId);
+    }
+
+    private void initializeDefaultsForSystemApps(int userId) {
+        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        List<PackageInfo> packages = getContext().getPackageManager().getInstalledPackagesAsUser(
+                PackageManager.MATCH_DISABLED_COMPONENTS
+                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                userId);
+        final int packageCount = packages.size();
+        for (int i = 0; i < packageCount; i++) {
+            final PackageInfo pi = packages.get(i);
+            String packageName = pi.packageName;
+            if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+                mAppIdleHistory.reportUsageLocked(packageName, userId, elapsedRealtime);
+            }
+        }
     }
 
     private void cleanUpRemovedUsersLocked() {
@@ -350,7 +365,7 @@
         if (timeLeft < 0) {
             timeLeft = 0;
         }
-        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft / 10);
+        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
     }
 
     private void postParoleEndTimeout() {
@@ -400,28 +415,27 @@
             return;
         }
 
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
         for (int i = 0; i < userIds.length; i++) {
             final int userId = userIds[i];
             List<PackageInfo> packages =
                     getContext().getPackageManager().getInstalledPackagesAsUser(
-                            PackageManager.GET_DISABLED_COMPONENTS
-                                | PackageManager.GET_UNINSTALLED_PACKAGES,
+                            PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
                             userId);
             synchronized (mLock) {
-                final long timeNow = checkAndGetTimeLocked();
-                final long screenOnTime = getScreenOnTimeLocked();
-                UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId,
-                        timeNow);
                 final int packageCount = packages.size();
                 for (int p = 0; p < packageCount; p++) {
                     final PackageInfo pi = packages.get(p);
                     final String packageName = pi.packageName;
                     final boolean isIdle = isAppIdleFiltered(packageName,
                             UserHandle.getAppId(pi.applicationInfo.uid),
-                            userId, service, timeNow, screenOnTime);
+                            userId, elapsedRealtime);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
                             userId, isIdle ? 1 : 0, packageName));
-                    mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
+                    if (isIdle) {
+                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+                    }
                 }
             }
         }
@@ -458,62 +472,6 @@
         }
     }
 
-    void updateDisplayLocked() {
-        boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
-                == Display.STATE_ON;
-
-        if (screenOn == mScreenOn) return;
-
-        mScreenOn = screenOn;
-        long now = SystemClock.elapsedRealtime();
-        if (mScreenOn) {
-            mLastScreenOnEventRealtime = now;
-        } else {
-            mScreenOnTime += now - mLastScreenOnEventRealtime;
-            writeScreenOnTimeLocked(mScreenOnTime);
-        }
-    }
-
-    long getScreenOnTimeLocked() {
-        long screenOnTime = mScreenOnTime;
-        if (mScreenOn) {
-            screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime;
-        }
-        return screenOnTime;
-    }
-
-    private File getScreenOnTimeFile() {
-        return new File(mUsageStatsDir, UserHandle.USER_SYSTEM + "/screen_on_time");
-    }
-
-    private long readScreenOnTimeLocked() {
-        long screenOnTime = 0;
-        File screenOnTimeFile = getScreenOnTimeFile();
-        if (screenOnTimeFile.exists()) {
-            try {
-                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
-                screenOnTime = Long.parseLong(reader.readLine());
-                reader.close();
-            } catch (IOException | NumberFormatException e) {
-            }
-        } else {
-            writeScreenOnTimeLocked(screenOnTime);
-        }
-        return screenOnTime;
-    }
-
-    private void writeScreenOnTimeLocked(long screenOnTime) {
-        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
-        FileOutputStream fos = null;
-        try {
-            fos = screenOnTimeFile.startWrite();
-            fos.write(Long.toString(screenOnTime).getBytes());
-            screenOnTimeFile.finishWrite(fos);
-        } catch (IOException ioe) {
-            screenOnTimeFile.failWrite(fos);
-        }
-    }
-
     void onDeviceIdleModeChanged() {
         final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
         if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
@@ -549,7 +507,7 @@
         if (service == null) {
             service = new UserUsageStatsService(getContext(), userId,
                     new File(mUsageStatsDir, Integer.toString(userId)), this);
-            service.init(currentTimeMillis, getScreenOnTimeLocked());
+            service.init(currentTimeMillis);
             mUserState.put(userId, service);
         }
         return service;
@@ -569,8 +527,7 @@
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
                 final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(),
-                        false);
+                service.onTimeChanged(expectedSystemTime, actualSystemTime);
             }
             mRealTimeSnapshot = actualRealtime;
             mSystemTimeSnapshot = actualSystemTime;
@@ -602,26 +559,26 @@
     void reportEvent(UsageEvents.Event event, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked();
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             convertToSystemTimeLocked(event);
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long beginIdleTime = service.getBeginIdleTime(event.mPackage);
-            final long lastUsedTime = service.getSystemLastUsedTime(event.mPackage);
-            final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
-                    lastUsedTime, screenOnTime, timeNow);
-            service.reportEvent(event, screenOnTime);
+            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+            // about apps that are on some kind of whitelist anyway.
+            final boolean previouslyIdle = mAppIdleHistory.isIdleLocked(
+                    event.mPackage, userId, elapsedRealtime);
+            service.reportEvent(event);
             // Inform listeners if necessary
             if ((event.mEventType == Event.MOVE_TO_FOREGROUND
                     || event.mEventType == Event.MOVE_TO_BACKGROUND
                     || event.mEventType == Event.SYSTEM_INTERACTION
                     || event.mEventType == Event.USER_INTERACTION)) {
+                mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime);
                 if (previouslyIdle) {
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                             /* idle = */ 0, event.mPackage));
                     notifyBatteryStats(event.mPackage, userId, false);
-                    mAppIdleHistory.addEntry(event.mPackage, userId, false, timeNow);
                 }
             }
         }
@@ -655,28 +612,23 @@
      * the threshold for idle.
      */
     void forceIdleState(String packageName, int userId, boolean idle) {
+        final int appId = getAppId(packageName);
+        if (appId < 0) return;
         synchronized (mLock) {
-            final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked();
-            final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long beginIdleTime = service.getBeginIdleTime(packageName);
-            final long lastUsedTime = service.getSystemLastUsedTime(packageName);
-            final boolean previouslyIdle = hasPassedIdleTimeoutLocked(beginIdleTime,
-                    lastUsedTime, screenOnTime, timeNow);
-            service.setBeginIdleTime(packageName, deviceUsageTime);
-            service.setSystemLastUsedTime(packageName,
-                    timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
+            final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+                    userId, elapsedRealtime);
+            mAppIdleHistory.setIdleLocked(packageName, userId, idle, elapsedRealtime);
+            final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+                    userId, elapsedRealtime);
             // Inform listeners if necessary
-            if (previouslyIdle != idle) {
+            if (previouslyIdle != stillIdle) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                        /* idle = */ idle ? 1 : 0, packageName));
-                if (!idle) {
+                        /* idle = */ stillIdle ? 1 : 0, packageName));
+                if (!stillIdle) {
                     notifyBatteryStats(packageName, userId, idle);
                 }
-                mAppIdleHistory.addEntry(packageName, userId, idle, timeNow);
             }
         }
     }
@@ -693,10 +645,11 @@
     /**
      * Called by the Binder stub.
      */
-    void removeUser(int userId) {
+    void onUserRemoved(int userId) {
         synchronized (mLock) {
             Slog.i(TAG, "Removing user " + userId + " and all data.");
             mUserState.remove(userId);
+            mAppIdleHistory.onUserRemoved(userId);
             cleanUpRemovedUsersLocked();
         }
     }
@@ -750,29 +703,12 @@
         }
     }
 
-    private boolean isAppIdleUnfiltered(String packageName, UserUsageStatsService userService,
-            long timeNow, long screenOnTime) {
+    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
         synchronized (mLock) {
-            long beginIdleTime = userService.getBeginIdleTime(packageName);
-            long lastUsedTime = userService.getSystemLastUsedTime(packageName);
-            return hasPassedIdleTimeoutLocked(beginIdleTime, lastUsedTime, screenOnTime,
-                    timeNow);
+            return mAppIdleHistory.isIdleLocked(packageName, userId, elapsedRealtime);
         }
     }
 
-    /**
-     * @param beginIdleTime when the app was last used in device usage timebase
-     * @param lastUsedTime wallclock time of when the app was last used
-     * @param screenOnTime screen-on timebase time
-     * @param currentTime current time in device usage timebase
-     * @return whether it's been used far enough in the past to be considered inactive
-     */
-    boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime,
-            long screenOnTime, long currentTime) {
-        return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis)
-                && (lastUsedTime <= currentTime - mAppIdleWallclockThresholdMillis);
-    }
-
     void addListener(AppIdleStateChangeListener listener) {
         synchronized (mLock) {
             if (!mPackageAccessListeners.contains(listener)) {
@@ -787,32 +723,22 @@
         }
     }
 
-    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) {
+    int getAppId(String packageName) {
+        try {
+            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
+            return ai.uid;
+        } catch (NameNotFoundException re) {
+            return -1;
+        }
+    }
+
+    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) {
         if (mAppIdleParoled) {
             return false;
         }
-        try {
-            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_UNINSTALLED_PACKAGES
-                            | PackageManager.GET_DISABLED_COMPONENTS);
-            return isAppIdleFiltered(packageName, ai.uid, userId, timeNow);
-        } catch (PackageManager.NameNotFoundException e) {
-        }
-        return false;
-    }
-
-    boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) {
-        final UserUsageStatsService userService;
-        final long screenOnTime;
-        synchronized (mLock) {
-            if (timeNow == -1) {
-                timeNow = checkAndGetTimeLocked();
-            }
-            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked();
-        }
-        return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
-                userService, timeNow, screenOnTime);
+        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
     }
 
     /**
@@ -822,7 +748,7 @@
      * Called by interface impls.
      */
     private boolean isAppIdleFiltered(String packageName, int appId, int userId,
-            UserUsageStatsService userService, long timeNow, long screenOnTime) {
+            long elapsedRealtime) {
         if (packageName == null) return false;
         // If not enabled at all, of course nobody is ever idle.
         if (!mAppIdleEnabled) {
@@ -864,7 +790,7 @@
             return false;
         }
 
-        return isAppIdleUnfiltered(packageName, userService, timeNow, screenOnTime);
+        return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
     }
 
     int[] getIdleUidsForUser(int userId) {
@@ -872,14 +798,7 @@
             return new int[0];
         }
 
-        final long timeNow;
-        final UserUsageStatsService userService;
-        final long screenOnTime;
-        synchronized (mLock) {
-            timeNow = checkAndGetTimeLocked();
-            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked();
-        }
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
 
         List<ApplicationInfo> apps;
         try {
@@ -899,12 +818,12 @@
 
         // Now resolve all app state.  Iterating over all apps, keeping track of how many
         // we find for each uid and how many of those are idle.
-        for (int i = apps.size()-1; i >= 0; i--) {
+        for (int i = apps.size() - 1; i >= 0; i--) {
             ApplicationInfo ai = apps.get(i);
 
             // Check whether this app is idle.
             boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
-                    userId, userService, timeNow, screenOnTime);
+                    userId, elapsedRealtime);
 
             int index = uidStates.indexOfKey(ai.uid);
             if (index < 0) {
@@ -990,8 +909,11 @@
         for (int i = 0; i < userCount; i++) {
             UserUsageStatsService service = mUserState.valueAt(i);
             service.persistActiveStats();
+            mAppIdleHistory.writeAppIdleTimesLocked(mUserState.keyAt(i));
         }
-
+        // Persist elapsed time periodically, in case screen doesn't get toggled
+        // until the next boot
+        mAppIdleHistory.writeElapsedTimeLocked();
         mHandler.removeMessages(MSG_FLUSH_TO_DISK);
     }
 
@@ -1000,7 +922,6 @@
      */
     void dump(String[] args, PrintWriter pw) {
         synchronized (mLock) {
-            final long screenOnTime = getScreenOnTimeLocked();
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
             ArraySet<String> argSet = new ArraySet<>();
             argSet.addAll(Arrays.asList(args));
@@ -1011,27 +932,28 @@
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
-                    mUserState.valueAt(i).checkin(idpw, screenOnTime);
+                    mUserState.valueAt(i).checkin(idpw);
                 } else {
-                    mUserState.valueAt(i).dump(idpw, screenOnTime);
+                    mUserState.valueAt(i).dump(idpw);
                     idpw.println();
-                    if (args.length > 0 && "history".equals(args[0])) {
-                        mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
+                    if (args.length > 0) {
+                        if ("history".equals(args[0])) {
+                            mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
+                        } else if ("flush".equals(args[0])) {
+                            UsageStatsService.this.flushToDiskLocked();
+                            pw.println("Flushed stats to disk");
+                        }
                     }
                 }
+                mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
                 idpw.decreaseIndent();
             }
-            pw.print("Screen On Timebase: ");
-            pw.print(screenOnTime);
-            pw.print(" (");
-            TimeUtils.formatDuration(screenOnTime, pw);
-            pw.println(")");
 
             pw.println();
             pw.println("Settings:");
 
             pw.print("  mAppIdleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleDurationMillis, pw);
+            TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
             pw.println();
 
             pw.print("  mAppIdleWallclockThresholdMillis=");
@@ -1057,11 +979,6 @@
             pw.print("mLastAppIdleParoledTime=");
             TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
             pw.println();
-            pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw);
-            pw.println();
-            pw.print("mLastScreenOnEventRealtime=");
-            TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw);
-            pw.println();
         }
     }
 
@@ -1082,7 +999,7 @@
                     break;
 
                 case MSG_REMOVE_USER:
-                    removeUser(msg.arg1);
+                    onUserRemoved(msg.arg1);
                     break;
 
                 case MSG_INFORM_LISTENERS:
@@ -1179,13 +1096,13 @@
                 }
 
                 // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleDurationMillis = mParser.getLong(KEY_IDLE_DURATION,
+                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
 
                 mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
                         COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
 
-                mCheckIdleIntervalMillis = Math.min(mAppIdleDurationMillis / 4,
+                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
                         COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
 
                 // Default: 24 hours between paroles
@@ -1194,6 +1111,8 @@
 
                 mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+                        mAppIdleScreenThresholdMillis);
             }
         }
     }
@@ -1284,7 +1203,8 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, -1);
+                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+                        SystemClock.elapsedRealtime());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1304,11 +1224,9 @@
                     "No permission to change app idle state");
             final long token = Binder.clearCallingIdentity();
             try {
-                PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(packageName,
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-                if (pi == null) return;
+                final int appId = getAppId(packageName);
+                if (appId < 0) return;
                 UsageStatsService.this.setAppIdle(packageName, idle, userId);
-            } catch (RemoteException re) {
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1335,8 +1253,6 @@
             }
             UsageStatsService.this.dump(args, pw);
         }
-
-
     }
 
     /**
@@ -1411,7 +1327,8 @@
 
         @Override
         public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
-            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1);
+            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
+                    SystemClock.elapsedRealtime());
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index f2ca3a4..c95ff23 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,7 +26,6 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
-import android.text.TextUtils;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -55,13 +54,11 @@
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
-    private static final String LAST_TIME_ACTIVE_SYSTEM_ATTR = "lastTimeActiveSystem";
-    private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime";
     private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
 
     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
-            throws XmlPullParserException, IOException {
+            throws IOException {
         final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
         if (pkg == null) {
             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
@@ -72,20 +69,6 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-
-        final String lastTimeUsedSystem = parser.getAttributeValue(null,
-                LAST_TIME_ACTIVE_SYSTEM_ATTR);
-        if (TextUtils.isEmpty(lastTimeUsedSystem)) {
-            // If the field isn't present, use the old one.
-            stats.mLastTimeSystemUsed = stats.mLastTimeUsed;
-        } else {
-            stats.mLastTimeSystemUsed = statsOut.beginTime + Long.parseLong(lastTimeUsedSystem);
-        }
-
-        final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR);
-        if (!TextUtils.isEmpty(beginIdleTime)) {
-            stats.mBeginIdleTime = Long.parseLong(beginIdleTime);
-        }
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
     }
@@ -141,13 +124,10 @@
         // Write the time offset.
         XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
                 usageStats.mLastTimeUsed - stats.beginTime);
-        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_SYSTEM_ATTR,
-                usageStats.mLastTimeSystemUsed - stats.beginTime);
 
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
-        XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime);
 
         xml.endTag(null, PACKAGE_TAG);
     }
@@ -255,7 +235,6 @@
         }
         xml.endTag(null, PACKAGES_TAG);
 
-
         xml.startTag(null, CONFIGURATIONS_TAG);
         final int configCount = stats.configurations.size();
         for (int i = 0; i < configCount; i++) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f2045d3..7d003f3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,7 +59,6 @@
     private final Context mContext;
     private final UsageStatsDatabase mDatabase;
     private final IntervalStats[] mCurrentStats;
-    private IntervalStats mAppIdleRollingWindow;
     private boolean mStatsChanged = false;
     private final UnixCalendar mDailyExpiryDate;
     private final StatsUpdatedListener mListener;
@@ -74,7 +73,11 @@
     interface StatsUpdatedListener {
         void onStatsUpdated();
         void onStatsReloaded();
-        long getAppIdleRollingWindowDurationMillis();
+        /**
+         * Callback that a system update was detected
+         * @param mUserId user that needs to be initialized
+         */
+        void onNewUpdate(int mUserId);
     }
 
     UserUsageStatsService(Context context, int userId, File usageStatsDir,
@@ -88,7 +91,7 @@
         mUserId = userId;
     }
 
-    void init(final long currentTimeMillis, final long deviceUsageTime) {
+    void init(final long currentTimeMillis) {
         mDatabase.init(currentTimeMillis);
 
         int nullCount = 0;
@@ -112,7 +115,7 @@
 
             // By calling loadActiveStats, we will
             // generate new stats for each bucket.
-            loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
+            loadActiveStats(currentTimeMillis);
         } else {
             // Set up the expiry date to be one day from the latest daily stat.
             // This may actually be today and we will rollover on the first event
@@ -136,54 +139,18 @@
             stat.updateConfigurationStats(null, stat.lastTimeSaved);
         }
 
-        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
-
         if (mDatabase.isNewUpdate()) {
-            initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
-                    mDatabase.isFirstUpdate());
+            notifyNewUpdate();
         }
     }
 
-    /**
-     * If any of the apps don't have a last-used entry, add one now.
-     * @param currentTimeMillis the current time
-     * @param firstUpdate if it is the first update, touch all installed apps, otherwise only
-     *        touch the system apps
-     */
-    private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
-            boolean firstUpdate) {
-        PackageManager pm = mContext.getPackageManager();
-        List<PackageInfo> packages = pm.getInstalledPackagesAsUser(0, mUserId);
-        final int packageCount = packages.size();
-        for (int i = 0; i < packageCount; i++) {
-            final PackageInfo pi = packages.get(i);
-            String packageName = pi.packageName;
-            if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
-                    && getBeginIdleTime(packageName) == -1) {
-                for (IntervalStats stats : mCurrentStats) {
-                    stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
-                    stats.updateBeginIdleTime(packageName, deviceUsageTime);
-                }
-
-                mAppIdleRollingWindow.update(packageName, currentTimeMillis,
-                        Event.SYSTEM_INTERACTION);
-                mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime);
-                mStatsChanged = true;
-            }
-        }
-        // Persist the new OTA-related access stats.
-        persistActiveStats();
-    }
-
-    void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
-                       boolean resetBeginIdleTime) {
+    void onTimeChanged(long oldTime, long newTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
-        loadActiveStats(newTime, resetBeginIdleTime);
-        refreshAppIdleRollingWindow(newTime, deviceUsageTime);
+        loadActiveStats(newTime);
     }
 
-    void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
+    void reportEvent(UsageEvents.Event event) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                     + "[" + event.mTimeStamp + "]: "
@@ -192,7 +159,7 @@
 
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
-            rolloverStats(event.mTimeStamp, deviceUsageTime);
+            rolloverStats(event.mTimeStamp);
         }
 
         final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
@@ -218,35 +185,9 @@
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
-                stats.updateBeginIdleTime(event.mPackage, deviceUsageTime);
             }
         }
 
-        if (event.mEventType != Event.CONFIGURATION_CHANGE) {
-            mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
-            mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
-        }
-
-        notifyStatsChanged();
-    }
-
-    /**
-     * Sets the beginIdleTime for each of the intervals.
-     * @param beginIdleTime
-     */
-    void setBeginIdleTime(String packageName, long beginIdleTime) {
-        for (IntervalStats stats : mCurrentStats) {
-            stats.updateBeginIdleTime(packageName, beginIdleTime);
-        }
-        mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
-        notifyStatsChanged();
-    }
-
-    void setSystemLastUsedTime(String packageName, long lastUsedTime) {
-        for (IntervalStats stats : mCurrentStats) {
-            stats.updateSystemLastUsedTime(packageName, lastUsedTime);
-        }
-        mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
         notifyStatsChanged();
     }
 
@@ -404,24 +345,6 @@
         return new UsageEvents(results, table);
     }
 
-    long getBeginIdleTime(String packageName) {
-        UsageStats packageUsage;
-        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
-            return -1;
-        } else {
-            return packageUsage.getBeginIdleTime();
-        }
-    }
-
-    long getSystemLastUsedTime(String packageName) {
-        UsageStats packageUsage;
-        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
-            return -1;
-        } else {
-            return packageUsage.getLastTimeSystemUsed();
-        }
-    }
-
     void persistActiveStats() {
         if (mStatsChanged) {
             Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
@@ -436,7 +359,7 @@
         }
     }
 
-    private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
+    private void rolloverStats(final long currentTimeMillis) {
         final long startTime = SystemClock.elapsedRealtime();
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
 
@@ -463,7 +386,7 @@
 
         persistActiveStats();
         mDatabase.prune(currentTimeMillis);
-        loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
+        loadActiveStats(currentTimeMillis);
 
         final int continueCount = continuePreviousDay.size();
         for (int i = 0; i < continueCount; i++) {
@@ -477,8 +400,6 @@
         }
         persistActiveStats();
 
-        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
-
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
                 + " milliseconds");
@@ -491,7 +412,11 @@
         }
     }
 
-    private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
+    private void notifyNewUpdate() {
+        mListener.onNewUpdate(mUserId);
+    }
+
+    private void loadActiveStats(final long currentTimeMillis) {
         for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
             final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
             if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
@@ -514,12 +439,6 @@
                 mCurrentStats[intervalType].beginTime = currentTimeMillis;
                 mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
             }
-
-            if (resetBeginIdleTime) {
-                for (UsageStats usageStats : mCurrentStats[intervalType].packageStats.values()) {
-                    usageStats.mBeginIdleTime = 0;
-                }
-            }
         }
 
         mStatsChanged = false;
@@ -538,96 +457,28 @@
                 mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
-    private static void mergePackageStats(IntervalStats dst, IntervalStats src,
-                                          final long deviceUsageTime) {
-        dst.endTime = Math.max(dst.endTime, src.endTime);
-
-        final int srcPackageCount = src.packageStats.size();
-        for (int i = 0; i < srcPackageCount; i++) {
-            final String packageName = src.packageStats.keyAt(i);
-            final UsageStats srcStats = src.packageStats.valueAt(i);
-            UsageStats dstStats = dst.packageStats.get(packageName);
-            if (dstStats == null) {
-                dstStats = new UsageStats(srcStats);
-                dst.packageStats.put(packageName, dstStats);
-            } else {
-                dstStats.add(src.packageStats.valueAt(i));
-            }
-
-            // App idle times can not begin in the future. This happens if we had a time change.
-            if (dstStats.mBeginIdleTime > deviceUsageTime) {
-                dstStats.mBeginIdleTime = deviceUsageTime;
-            }
-        }
-    }
-
-    /**
-     * App idle operates on a rolling window of time. When we roll over time, we end up with a
-     * period of time where in-memory stats are empty and we don't hit the disk for older stats
-     * for performance reasons. Suddenly all apps will become idle.
-     *
-     * Instead, at times we do a deep query to find all the apps that have run in the past few
-     * days and keep the cached data up to date.
-     *
-     * @param currentTimeMillis
-     */
-    void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
-        // Start the rolling window for AppIdle requests.
-        final long startRangeMillis = currentTimeMillis -
-                mListener.getAppIdleRollingWindowDurationMillis();
-
-        List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
-                startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() {
-                    @Override
-                    public void combine(IntervalStats stats, boolean mutable,
-                                        List<IntervalStats> accumulatedResult) {
-                        IntervalStats accum;
-                        if (accumulatedResult.isEmpty()) {
-                            accum = new IntervalStats();
-                            accum.beginTime = stats.beginTime;
-                            accumulatedResult.add(accum);
-                        } else {
-                            accum = accumulatedResult.get(0);
-                        }
-
-                        mergePackageStats(accum, stats, deviceUsageTime);
-                    }
-                });
-
-        if (stats == null || stats.isEmpty()) {
-            mAppIdleRollingWindow = new IntervalStats();
-            mergePackageStats(mAppIdleRollingWindow,
-                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
-        } else {
-            mAppIdleRollingWindow = stats.get(0);
-        }
-    }
-
     //
     // -- DUMP related methods --
     //
 
-    void checkin(final IndentingPrintWriter pw, final long screenOnTime) {
+    void checkin(final IndentingPrintWriter pw) {
         mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
             @Override
             public boolean checkin(IntervalStats stats) {
-                printIntervalStats(pw, stats, screenOnTime, false);
+                printIntervalStats(pw, stats, false);
                 return true;
             }
         });
     }
 
-    void dump(IndentingPrintWriter pw, final long screenOnTime) {
+    void dump(IndentingPrintWriter pw) {
         // This is not a check-in, only dump in-memory stats.
         for (int interval = 0; interval < mCurrentStats.length; interval++) {
             pw.print("In-memory ");
             pw.print(intervalToString(interval));
             pw.println(" stats");
-            printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
+            printIntervalStats(pw, mCurrentStats[interval], true);
         }
-
-        pw.println("AppIdleRollingWindow cache");
-        printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
     }
 
     private String formatDateTime(long dateTime, boolean pretty) {
@@ -644,7 +495,7 @@
         return Long.toString(elapsedTime);
     }
 
-    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime,
+    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
             boolean prettyDates) {
         if (prettyDates) {
             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
@@ -665,10 +516,6 @@
             pw.printPair("totalTime",
                     formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
             pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
-            pw.printPair("lastTimeSystem",
-                    formatDateTime(usageStats.mLastTimeSystemUsed, prettyDates));
-            pw.printPair("inactiveTime",
-                    formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates));
             pw.println();
         }
         pw.decreaseIndent();
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
index 13efc36..fa666af 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -26,7 +26,7 @@
 oneway interface IWifiNanEventListener
 {
     void onConfigCompleted(in ConfigRequest completedConfig);
-    void onConfigFailed(int reason);
+    void onConfigFailed(in ConfigRequest failedConfig, int reason);
     void onNanDown(int reason);
     void onIdentityChanged();
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index ec9e462..f382d97 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
index 50c34d9..d60d8ca 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
index eae0a55..5c18bd7 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -47,7 +47,8 @@
 
     /**
      * Configuration failed callback event registration flag. Corresponding
-     * callback is {@link WifiNanEventListener#onConfigFailed(int)}.
+     * callback is
+     * {@link WifiNanEventListener#onConfigFailed(ConfigRequest, int)}.
      */
     public static final int LISTEN_CONFIG_FAILED = 0x1 << 1;
 
@@ -93,7 +94,7 @@
                         WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj);
                         break;
                     case LISTEN_CONFIG_FAILED:
-                        WifiNanEventListener.this.onConfigFailed(msg.arg1);
+                        WifiNanEventListener.this.onConfigFailed((ConfigRequest) msg.obj, msg.arg1);
                         break;
                     case LISTEN_NAN_DOWN:
                         WifiNanEventListener.this.onNanDown(msg.arg1);
@@ -129,7 +130,7 @@
      *
      * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}.
      */
-    public void onConfigFailed(int reason) {
+    public void onConfigFailed(ConfigRequest failedConfig, int reason) {
         Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable");
     }
 
@@ -173,11 +174,14 @@
         }
 
         @Override
-        public void onConfigFailed(int reason) {
-            if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason);
+        public void onConfigFailed(ConfigRequest failedConfig, int reason) {
+            if (VDBG) {
+                Log.v(TAG, "onConfigFailed: failedConfig=" + failedConfig + ", reason=" + reason);
+            }
 
             Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED);
             msg.arg1 = reason;
+            msg.obj = failedConfig;
             mHandler.sendMessage(msg);
         }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index d5e59f0..0925087 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -303,8 +303,8 @@
      * message). Override to implement your custom response.
      * <p>
      * Note that either this callback or
-     * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received -
-     * never both.
+     * {@link WifiNanSessionListener#onMessageSendFail(int, int)} will be
+     * received - never both.
      */
     public void onMessageSendSuccess(int messageId) {
         if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
@@ -319,8 +319,8 @@
      * message). Override to implement your custom response.
      * <p>
      * Note that either this callback or
-     * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received -
-     * never both
+     * {@link WifiNanSessionListener#onMessageSendSuccess(int)} will be received
+     * - never both
      *
      * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
      *            codes.