Merge "Fix clip rect when drawing accessibility focus rect" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index a69a8e5..f7c0b97 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5391,8 +5391,8 @@
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
-    field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.ACTION_LOCK_TASK_ENTERING";
-    field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.ACTION_LOCK_TASK_EXITING";
+    field public static final java.lang.String ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING";
+    field public static final java.lang.String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING";
     field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED";
     field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
     field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
@@ -5706,6 +5706,19 @@
 
 package android.app.usage {
 
+  public final class ConfigurationStats implements android.os.Parcelable {
+    ctor public ConfigurationStats(android.app.usage.ConfigurationStats);
+    method public int describeContents();
+    method public int getActivationCount();
+    method public android.content.res.Configuration getConfiguration();
+    method public long getFirstTimeStamp();
+    method public long getLastTimeActive();
+    method public long getLastTimeStamp();
+    method public long getTotalTimeActive();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
   public final class UsageEvents implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -5718,9 +5731,11 @@
   public static final class UsageEvents.Event {
     ctor public UsageEvents.Event();
     method public java.lang.String getClassName();
+    method public android.content.res.Configuration getConfiguration();
     method public int getEventType();
     method public java.lang.String getPackageName();
     method public long getTimeStamp();
+    field public static final int CONFIGURATION_CHANGE = 5; // 0x5
     field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
     field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
     field public static final int NONE = 0; // 0x0
@@ -5741,6 +5756,7 @@
 
   public final class UsageStatsManager {
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
+    method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
     method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
     field public static final int INTERVAL_BEST = 4; // 0x4
@@ -17306,6 +17322,7 @@
     method public android.net.Uri getPacFileUrl();
     method public int getPort();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
   }
 
   public abstract class PskKeyManager {
@@ -28888,6 +28905,7 @@
     field public static final java.lang.String MMS_CONFIG_ALLOW_ATTACH_AUDIO = "allowAttachAudio";
     field public static final java.lang.String MMS_CONFIG_APPEND_TRANSACTION_ID = "enabledTransID";
     field public static final java.lang.String MMS_CONFIG_EMAIL_GATEWAY_NUMBER = "emailGatewayNumber";
+    field public static final java.lang.String MMS_CONFIG_GROUP_MMS_ENABLED = "enableGroupMms";
     field public static final java.lang.String MMS_CONFIG_HTTP_PARAMS = "httpParams";
     field public static final java.lang.String MMS_CONFIG_HTTP_SOCKET_TIMEOUT = "httpSocketTimeout";
     field public static final java.lang.String MMS_CONFIG_MAX_IMAGE_HEIGHT = "maxImageHeight";
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f8dfdd9..5921477 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,7 +21,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -825,6 +824,13 @@
     public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
 
     /**
+     * {@link #extras} key: the user that built the notification.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
+
+    /**
      * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
      * displayed in the heads up space.
      *
@@ -1807,12 +1813,10 @@
                 = "android.rebuild.hudViewActionCount";
 
         /**
-         * The ApplicationInfo of the package that created the notification, used to create
-         * a context to rebuild the notification via a Builder.
-         * @hide
+         * The package name of the context used to create the notification via a Builder.
          */
-        private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO =
-                "android.rebuild.applicationInfo";
+        private static final String EXTRA_REBUILD_CONTEXT_PACKAGE =
+                "android.rebuild.contextPackage";
 
         // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
         // memory intensive resources.
@@ -1863,6 +1867,11 @@
         private int mColor = COLOR_DEFAULT;
 
         /**
+         * The user that built the notification originally.
+         */
+        private int mOriginatingUserId;
+
+        /**
          * Contains extras related to rebuilding during the build phase.
          */
         private Bundle mRebuildBundle = new Bundle();
@@ -2582,7 +2591,7 @@
             // Note: This assumes that the current user can read the profile badge of the
             // originating user.
             UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            return userManager.getBadgeForUser(new UserHandle(mContext.getUserId()), 0);
+            return userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
         }
 
         private Bitmap getProfileBadge() {
@@ -2661,7 +2670,8 @@
          * @param hasProgress whether the progress bar should be shown and set
          */
         private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
-            RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
+            RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(),
+                    mOriginatingUserId, resId);
 
             resetStandardTemplate(contentView);
 
@@ -3053,8 +3063,8 @@
          */
         public void populateExtras(Bundle extras) {
             // Store original information used in the construction of this object
-            extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO,
-                    mContext.getApplicationInfo());
+            extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
+            extras.putString(EXTRA_REBUILD_CONTEXT_PACKAGE, mContext.getPackageName());
             extras.putCharSequence(EXTRA_TITLE, mContentTitle);
             extras.putCharSequence(EXTRA_TEXT, mContentText);
             extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
@@ -3142,14 +3152,13 @@
             extras.remove(EXTRA_NEEDS_REBUILD);
 
             // Re-create notification context so we can access app resources.
-            ApplicationInfo applicationInfo = extras.getParcelable(
-                    EXTRA_REBUILD_CONTEXT_APPLICATION_INFO);
+            String packageName = extras.getString(EXTRA_REBUILD_CONTEXT_PACKAGE);
             Context builderContext;
             try {
-                builderContext = context.createApplicationContext(applicationInfo,
+                builderContext = context.createPackageContext(packageName,
                         Context.CONTEXT_RESTRICTED);
             } catch (NameNotFoundException e) {
-                Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
+                Log.e(TAG, "Package name " + packageName + " not found");
                 builderContext = context;  // try with our context
             }
 
@@ -3284,6 +3293,7 @@
 
             // Extras.
             Bundle extras = n.extras;
+            mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
             mContentTitle = extras.getCharSequence(EXTRA_TITLE);
             mContentText = extras.getCharSequence(EXTRA_TEXT);
             mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
@@ -3316,6 +3326,7 @@
          * object.
          */
         public Notification build() {
+            mOriginatingUserId = mContext.getUserId();
             mHasThreeLines = hasThreeLines();
 
             Notification n = buildUnstyled();
@@ -4815,8 +4826,8 @@
             super(parcel);
         }
 
-        public BuilderRemoteViews(ApplicationInfo appInfo, int layoutId) {
-            super(appInfo, layoutId);
+        public BuilderRemoteViews(String packageName, int userId, int layoutId) {
+            super(packageName, userId, layoutId);
         }
 
         @Override
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 15def09..e2f175c 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -177,7 +177,7 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_LOCK_TASK_ENTERING
-            = "android.app.action.ACTION_LOCK_TASK_ENTERING";
+            = "android.app.action.LOCK_TASK_ENTERING";
 
     /**
      * Action sent to a device administrator to notify that the device is exiting
@@ -190,7 +190,7 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_LOCK_TASK_EXITING
-            = "android.app.action.ACTION_LOCK_TASK_EXITING";
+            = "android.app.action.LOCK_TASK_EXITING";
 
     /**
      * A boolean describing whether the device is currently entering or exiting
diff --git a/core/java/android/app/usage/ConfigurationStats.java b/core/java/android/app/usage/ConfigurationStats.java
new file mode 100644
index 0000000..080216c
--- /dev/null
+++ b/core/java/android/app/usage/ConfigurationStats.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (C) 2014 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.app.usage;
+
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the usage statistics of a device {@link android.content.res.Configuration} for a
+ * specific time range.
+ */
+public final class ConfigurationStats implements Parcelable {
+
+    /**
+     * {@hide}
+     */
+    public Configuration mConfiguration;
+
+    /**
+     * {@hide}
+     */
+    public long mBeginTimeStamp;
+
+    /**
+     * {@hide}
+     */
+    public long mEndTimeStamp;
+
+    /**
+     * {@hide}
+     */
+    public long mLastTimeActive;
+
+    /**
+     * {@hide}
+     */
+    public long mTotalTimeActive;
+
+    /**
+     * {@hide}
+     */
+    public int mActivationCount;
+
+    /**
+     * {@hide}
+     */
+    public ConfigurationStats() {
+    }
+
+    public ConfigurationStats(ConfigurationStats stats) {
+        mConfiguration = stats.mConfiguration;
+        mBeginTimeStamp = stats.mBeginTimeStamp;
+        mEndTimeStamp = stats.mEndTimeStamp;
+        mLastTimeActive = stats.mLastTimeActive;
+        mTotalTimeActive = stats.mTotalTimeActive;
+        mActivationCount = stats.mActivationCount;
+    }
+
+    public Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    /**
+     * Get the beginning of the time range this {@link ConfigurationStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getFirstTimeStamp() {
+        return mBeginTimeStamp;
+    }
+
+    /**
+     * Get the end of the time range this {@link ConfigurationStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeStamp() {
+        return mEndTimeStamp;
+    }
+
+    /**
+     * Get the last time this configuration was active, measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeActive() {
+        return mLastTimeActive;
+    }
+
+    /**
+     * Get the total time this configuration was active, measured in milliseconds.
+     */
+    public long getTotalTimeActive() {
+        return mTotalTimeActive;
+    }
+
+    /**
+     * Get the number of times this configuration was active.
+     */
+    public int getActivationCount() {
+        return mActivationCount;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mConfiguration != null) {
+            dest.writeInt(1);
+            mConfiguration.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+
+        dest.writeLong(mBeginTimeStamp);
+        dest.writeLong(mEndTimeStamp);
+        dest.writeLong(mLastTimeActive);
+        dest.writeLong(mTotalTimeActive);
+        dest.writeInt(mActivationCount);
+    }
+
+    public static final Creator<ConfigurationStats> CREATOR = new Creator<ConfigurationStats>() {
+        @Override
+        public ConfigurationStats createFromParcel(Parcel source) {
+            ConfigurationStats stats = new ConfigurationStats();
+            if (source.readInt() != 0) {
+                stats.mConfiguration = Configuration.CREATOR.createFromParcel(source);
+            }
+            stats.mBeginTimeStamp = source.readLong();
+            stats.mEndTimeStamp = source.readLong();
+            stats.mLastTimeActive = source.readLong();
+            stats.mTotalTimeActive = source.readLong();
+            stats.mActivationCount = source.readInt();
+            return stats;
+        }
+
+        @Override
+        public ConfigurationStats[] newArray(int size) {
+            return new ConfigurationStats[size];
+        }
+    };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 3b09888..4ed1489 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -27,5 +27,7 @@
 interface IUsageStatsManager {
     ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
             String callingPackage);
+    ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
+            String callingPackage);
     UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index fb80de2..1a947ec 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
 package android.app.usage;
 
 import android.content.ComponentName;
+import android.content.res.Configuration;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -63,6 +64,11 @@
         public static final int CONTINUE_PREVIOUS_DAY = 4;
 
         /**
+         * An event type denoting that the device configuration has changed.
+         */
+        public static final int CONFIGURATION_CHANGE = 5;
+
+        /**
          * {@hide}
          */
         public String mPackage;
@@ -83,6 +89,12 @@
         public int mEventType;
 
         /**
+         * Only present for {@link #CONFIGURATION_CHANGE} event types.
+         * {@hide}
+         */
+        public Configuration mConfiguration;
+
+        /**
          * TODO(adamlesinski): Removed before release.
          * {@hide}
          */
@@ -123,6 +135,14 @@
         public int getEventType() {
             return mEventType;
         }
+
+        /**
+         * Returns a {@link Configuration} for this event if the event is of type
+         * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
+         */
+        public Configuration getConfiguration() {
+            return mConfiguration;
+        }
     }
 
     // Only used when creating the resulting events. Not used for reading/unparceling.
@@ -201,23 +221,9 @@
             return false;
         }
 
-        final int packageIndex = mParcel.readInt();
-        if (packageIndex >= 0) {
-            eventOut.mPackage = mStringPool[packageIndex];
-        } else {
-            eventOut.mPackage = null;
-        }
+        readEventFromParcel(mParcel, eventOut);
 
-        final int classIndex = mParcel.readInt();
-        if (classIndex >= 0) {
-            eventOut.mClass = mStringPool[classIndex];
-        } else {
-            eventOut.mClass = null;
-        }
-        eventOut.mEventType = mParcel.readInt();
-        eventOut.mTimeStamp = mParcel.readLong();
         mIndex++;
-
         if (mIndex >= mEventCount) {
             mParcel.recycle();
             mParcel = null;
@@ -235,11 +241,6 @@
         }
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
     private int findStringIndex(String str) {
         final int index = Arrays.binarySearch(mStringPool, str);
         if (index < 0) {
@@ -248,6 +249,66 @@
         return index;
     }
 
+    /**
+     * Writes a single event to the parcel. Modify this when updating {@link Event}.
+     */
+    private void writeEventToParcel(Event event, Parcel p, int flags) {
+        final int packageIndex;
+        if (event.mPackage != null) {
+            packageIndex = findStringIndex(event.mPackage);
+        } else {
+            packageIndex = -1;
+        }
+
+        final int classIndex;
+        if (event.mClass != null) {
+            classIndex = findStringIndex(event.mClass);
+        } else {
+            classIndex = -1;
+        }
+        p.writeInt(packageIndex);
+        p.writeInt(classIndex);
+        p.writeInt(event.mEventType);
+        p.writeLong(event.mTimeStamp);
+
+        if (event.mEventType == Event.CONFIGURATION_CHANGE) {
+            event.mConfiguration.writeToParcel(p, flags);
+        }
+    }
+
+    /**
+     * Reads a single event from the parcel. Modify this when updating {@link Event}.
+     */
+    private void readEventFromParcel(Parcel p, Event eventOut) {
+        final int packageIndex = p.readInt();
+        if (packageIndex >= 0) {
+            eventOut.mPackage = mStringPool[packageIndex];
+        } else {
+            eventOut.mPackage = null;
+        }
+
+        final int classIndex = p.readInt();
+        if (classIndex >= 0) {
+            eventOut.mClass = mStringPool[classIndex];
+        } else {
+            eventOut.mClass = null;
+        }
+        eventOut.mEventType = p.readInt();
+        eventOut.mTimeStamp = p.readLong();
+
+        // Extract the configuration for configuration change events.
+        if (eventOut.mEventType == Event.CONFIGURATION_CHANGE) {
+            eventOut.mConfiguration = Configuration.CREATOR.createFromParcel(p);
+        } else {
+            eventOut.mConfiguration = null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mEventCount);
@@ -262,25 +323,9 @@
                     p.setDataPosition(0);
                     for (int i = 0; i < mEventCount; i++) {
                         final Event event = mEventsToWrite.get(i);
-
-                        final int packageIndex;
-                        if (event.mPackage != null) {
-                            packageIndex = findStringIndex(event.mPackage);
-                        } else {
-                            packageIndex = -1;
-                        }
-
-                        final int classIndex;
-                        if (event.mClass != null) {
-                            classIndex = findStringIndex(event.mClass);
-                        } else {
-                            classIndex = -1;
-                        }
-                        p.writeInt(packageIndex);
-                        p.writeInt(classIndex);
-                        p.writeInt(event.getEventType());
-                        p.writeLong(event.getTimeStamp());
+                        writeEventToParcel(event, p, flags);
                     }
+
                     final int listByteLength = p.dataPosition();
 
                     // Write the total length of the data.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5830fcf..bc6099a 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -125,9 +125,9 @@
      * @see #INTERVAL_YEARLY
      * @see #INTERVAL_BEST
      */
-    @SuppressWarnings("unchecked")
     public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
         try {
+            @SuppressWarnings("unchecked")
             ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
                     endTime, mContext.getOpPackageName());
             if (slice != null) {
@@ -136,7 +136,32 @@
         } catch (RemoteException e) {
             // fallthrough and return null.
         }
-        return Collections.EMPTY_LIST;
+        return Collections.emptyList();
+    }
+
+    /**
+     * Gets the hardware configurations the device was in for the given time range, aggregated by
+     * the specified interval. The results are ordered as in
+     * {@link #queryUsageStats(int, long, long)}.
+     *
+     * @param intervalType The time interval by which the stats are aggregated.
+     * @param beginTime The inclusive beginning of the range of stats to include in the results.
+     * @param endTime The exclusive end of the range of stats to include in the results.
+     * @return A list of {@link ConfigurationStats} or null if none are available.
+     */
+    public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime,
+            long endTime) {
+        try {
+            @SuppressWarnings("unchecked")
+            ParceledListSlice<ConfigurationStats> slice = mService.queryConfigurationStats(
+                    intervalType, beginTime, endTime, mContext.getOpPackageName());
+            if (slice != null) {
+                return slice.getList();
+            }
+        } catch (RemoteException e) {
+            // fallthrough and return the empty list.
+        }
+        return Collections.emptyList();
     }
 
     /**
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 119d705..083a48a 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -17,6 +17,7 @@
 package android.app.usage;
 
 import android.content.ComponentName;
+import android.content.res.Configuration;
 
 /**
  * UsageStatsManager local system service interface.
@@ -28,14 +29,19 @@
     /**
      * Reports an event to the UsageStatsManager.
      *
-     * @param component The component for which this event ocurred.
+     * @param component The component for which this event occurred.
      * @param userId The user id to which the component belongs to.
-     * @param timeStamp The time at which this event ocurred.
-     * @param eventType The event that occured. Valid values can be found at
+     * @param eventType The event that occurred. Valid values can be found at
      * {@link UsageEvents}
      */
-    public abstract void reportEvent(ComponentName component, int userId,
-            long timeStamp, int eventType);
+    public abstract void reportEvent(ComponentName component, int userId, int eventType);
+
+    /**
+     * Reports a configuration change to the UsageStatsManager.
+     *
+     * @param config The new device configuration.
+     */
+    public abstract void reportConfigurationChange(Configuration config, int userId);
 
     /**
      * Prepares the UsageStatsService for shutdown.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index e63fd07..27bbb24 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -16,6 +16,12 @@
 
 package android.content.res;
 
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import android.content.pm.ActivityInfo;
 import android.os.Build;
 import android.os.Parcel;
@@ -23,7 +29,7 @@
 import android.text.TextUtils;
 import android.view.View;
 
-import java.text.Format;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Locale;
 
@@ -1353,8 +1359,6 @@
      * Returns a string representation of the configuration that can be parsed
      * by build tools (like AAPT).
      *
-     *
-     *
      * @hide
      */
     public static String resourceQualifierString(Configuration config) {
@@ -1568,4 +1572,229 @@
         parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
         return TextUtils.join("-", parts);
     }
+
+    /**
+     * Generate a delta Configuration between <code>base</code> and <code>change</code>. The
+     * resulting delta can be used with {@link #updateFrom(Configuration)}.
+     * <p />
+     * Caveat: If the any of the Configuration's members becomes undefined, then
+     * {@link #updateFrom(Configuration)} will treat it as a no-op and not update that member.
+     *
+     * This is fine for device configurations as no member is ever undefined.
+     * {@hide}
+     */
+    public static Configuration generateDelta(Configuration base, Configuration change) {
+        final Configuration delta = new Configuration();
+        if (base.fontScale != change.fontScale) {
+            delta.fontScale = change.fontScale;
+        }
+
+        if (base.mcc != change.mcc) {
+            delta.mcc = change.mcc;
+        }
+
+        if (base.mnc != change.mnc) {
+            delta.mnc = change.mnc;
+        }
+
+        if ((base.locale == null && change.locale != null) ||
+                (base.locale != null && !base.locale.equals(change.locale)))  {
+            delta.locale = change.locale;
+        }
+
+        if (base.touchscreen != change.touchscreen) {
+            delta.touchscreen = change.touchscreen;
+        }
+
+        if (base.keyboard != change.keyboard) {
+            delta.keyboard = change.keyboard;
+        }
+
+        if (base.keyboardHidden != change.keyboardHidden) {
+            delta.keyboardHidden = change.keyboardHidden;
+        }
+
+        if (base.navigation != change.navigation) {
+            delta.navigation = change.navigation;
+        }
+
+        if (base.navigationHidden != change.navigationHidden) {
+            delta.navigationHidden = change.navigationHidden;
+        }
+
+        if (base.orientation != change.orientation) {
+            delta.orientation = change.orientation;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
+        }
+
+        if ((base.screenLayout & SCREENLAYOUT_LONG_MASK) !=
+                (change.screenLayout & SCREENLAYOUT_LONG_MASK)) {
+            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK;
+        }
+
+        if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
+            delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
+        }
+
+        if ((base.uiMode & UI_MODE_NIGHT_MASK) != (change.uiMode & UI_MODE_NIGHT_MASK)) {
+            delta.uiMode |= change.uiMode & UI_MODE_NIGHT_MASK;
+        }
+
+        if (base.screenWidthDp != change.screenWidthDp) {
+            delta.screenWidthDp = change.screenWidthDp;
+        }
+
+        if (base.screenHeightDp != change.screenHeightDp) {
+            delta.screenHeightDp = change.screenHeightDp;
+        }
+
+        if (base.smallestScreenWidthDp != change.smallestScreenWidthDp) {
+            delta.smallestScreenWidthDp = change.smallestScreenWidthDp;
+        }
+
+        if (base.densityDpi != change.densityDpi) {
+            delta.densityDpi = change.densityDpi;
+        }
+        return delta;
+    }
+
+    private static final String XML_ATTR_FONT_SCALE = "fs";
+    private static final String XML_ATTR_MCC = "mcc";
+    private static final String XML_ATTR_MNC = "mnc";
+    private static final String XML_ATTR_LOCALE = "locale";
+    private static final String XML_ATTR_TOUCHSCREEN = "touch";
+    private static final String XML_ATTR_KEYBOARD = "key";
+    private static final String XML_ATTR_KEYBOARD_HIDDEN = "keyHid";
+    private static final String XML_ATTR_HARD_KEYBOARD_HIDDEN = "hardKeyHid";
+    private static final String XML_ATTR_NAVIGATION = "nav";
+    private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
+    private static final String XML_ATTR_ORIENTATION = "ori";
+    private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
+    private static final String XML_ATTR_UI_MODE = "ui";
+    private static final String XML_ATTR_SCREEN_WIDTH = "width";
+    private static final String XML_ATTR_SCREEN_HEIGHT = "height";
+    private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
+    private static final String XML_ATTR_DENSITY = "density";
+
+    /**
+     * Reads the attributes corresponding to Configuration member fields from the Xml parser.
+     * The parser is expected to be on a tag which has Configuration attributes.
+     *
+     * @param parser The Xml parser from which to read attributes.
+     * @param configOut The Configuration to populate from the Xml attributes.
+     * {@hide}
+     */
+    public static void readXmlAttrs(XmlPullParser parser, Configuration configOut)
+            throws XmlPullParserException, IOException {
+        configOut.fontScale = Float.intBitsToFloat(
+                XmlUtils.readIntAttribute(parser, XML_ATTR_FONT_SCALE, 0));
+        configOut.mcc = XmlUtils.readIntAttribute(parser, XML_ATTR_MCC, 0);
+        configOut.mnc = XmlUtils.readIntAttribute(parser, XML_ATTR_MNC, 0);
+
+        final String localeStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALE);
+        if (localeStr != null) {
+            configOut.locale = Locale.forLanguageTag(localeStr);
+        }
+
+        configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
+                TOUCHSCREEN_UNDEFINED);
+        configOut.keyboard = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD,
+                KEYBOARD_UNDEFINED);
+        configOut.keyboardHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD_HIDDEN,
+                KEYBOARDHIDDEN_UNDEFINED);
+        configOut.hardKeyboardHidden =
+                XmlUtils.readIntAttribute(parser, XML_ATTR_HARD_KEYBOARD_HIDDEN,
+                        HARDKEYBOARDHIDDEN_UNDEFINED);
+        configOut.navigation = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION,
+                NAVIGATION_UNDEFINED);
+        configOut.navigationHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION_HIDDEN,
+                NAVIGATIONHIDDEN_UNDEFINED);
+        configOut.orientation = XmlUtils.readIntAttribute(parser, XML_ATTR_ORIENTATION,
+                ORIENTATION_UNDEFINED);
+        configOut.screenLayout = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_LAYOUT,
+                SCREENLAYOUT_UNDEFINED);
+        configOut.uiMode = XmlUtils.readIntAttribute(parser, XML_ATTR_UI_MODE, 0);
+        configOut.screenWidthDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_WIDTH,
+                SCREEN_WIDTH_DP_UNDEFINED);
+        configOut.screenHeightDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_HEIGHT,
+                SCREEN_HEIGHT_DP_UNDEFINED);
+        configOut.smallestScreenWidthDp =
+                XmlUtils.readIntAttribute(parser, XML_ATTR_SMALLEST_WIDTH,
+                        SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
+        configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
+                DENSITY_DPI_UNDEFINED);
+    }
+
+
+    /**
+     * Writes the Configuration's member fields as attributes into the XmlSerializer.
+     * The serializer is expected to have already started a tag so that attributes can be
+     * immediately written.
+     *
+     * @param xml The serializer to which to write the attributes.
+     * @param config The Configuration whose member fields to write.
+     * {@hide}
+     */
+    public static void writeXmlAttrs(XmlSerializer xml, Configuration config) throws IOException {
+        XmlUtils.writeIntAttribute(xml, XML_ATTR_FONT_SCALE,
+                Float.floatToIntBits(config.fontScale));
+        if (config.mcc != 0) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_MCC, config.mcc);
+        }
+        if (config.mnc != 0) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc);
+        }
+        if (config.locale != null) {
+            XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALE, config.locale.toLanguageTag());
+        }
+        if (config.touchscreen != TOUCHSCREEN_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen);
+        }
+        if (config.keyboard != KEYBOARD_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD, config.keyboard);
+        }
+        if (config.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD_HIDDEN, config.keyboardHidden);
+        }
+        if (config.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_HARD_KEYBOARD_HIDDEN,
+                    config.hardKeyboardHidden);
+        }
+        if (config.navigation != NAVIGATION_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION, config.navigation);
+        }
+        if (config.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION_HIDDEN, config.navigationHidden);
+        }
+        if (config.orientation != ORIENTATION_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_ORIENTATION, config.orientation);
+        }
+        if (config.screenLayout != SCREENLAYOUT_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout);
+        }
+        if (config.uiMode != 0) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode);
+        }
+        if (config.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_WIDTH, config.screenWidthDp);
+        }
+        if (config.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_HEIGHT, config.screenHeightDp);
+        }
+        if (config.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_SMALLEST_WIDTH, config.smallestScreenWidthDp);
+        }
+        if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
+        }
+    }
 }
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 7ea6bae..1534e2c 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -334,10 +334,6 @@
         dest.writeStringArray(mParsedExclusionList);
     }
 
-    /**
-     * Implement the Parcelable interface.
-     * @hide
-     */
     public static final Creator<ProxyInfo> CREATOR =
         new Creator<ProxyInfo>() {
             public ProxyInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 7dce348..2b53c48 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -727,10 +727,9 @@
         int[] runs = dirs.mDirections;
         int lineStart = getLineStart(line);
         for (int i = 0; i < runs.length; i += 2) {
-            int start = lineStart + (runs[i] & RUN_LENGTH_MASK);
-            // No need to test the end as an offset after the last run should return the value
-            // corresponding of the last run
-            if (offset >= start) {
+            int start = lineStart + runs[i];
+            int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
+            if (offset >= start && offset < limit) {
                 int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
                 return ((level & 1) != 0);
             }
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 1e411b4..9fec9a1 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -17,7 +17,6 @@
 package android.text.format;
 
 import android.content.Context;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -129,14 +128,8 @@
      * @return true if 24 hour time format is selected, false otherwise.
      */
     public static boolean is24HourFormat(Context context) {
-        // This method is called by View classes that can be used by RemoteViews
-        // and rendered in another user. The context therefore may reference
-        // a user that would require interact accross users to access. So
-        // use the user id we are running as.
-        // This is the case when we have widgets from a user profile added
-        // to the homescreen.
-        String value = Settings.System.getStringForUser(context.getContentResolver(),
-                Settings.System.TIME_12_24, UserHandle.myUserId());
+        String value = Settings.System.getString(context.getContentResolver(),
+                Settings.System.TIME_12_24);
 
         if (value == null) {
             Locale locale = context.getResources().getConfiguration().locale;
@@ -234,14 +227,8 @@
      * @return the {@link java.text.DateFormat} object that properly formats the date.
      */
     public static java.text.DateFormat getDateFormat(Context context) {
-        // This method is called by View classes that can be used by RemoteViews
-        // and rendered in another user. The context therefore may reference
-        // a user that would require interact accross users to access. So
-        // use the user id we are running as.
-        // This is the case when we have widgets from a user profile added
-        // to the homescreen.
-        String value = Settings.System.getStringForUser(context.getContentResolver(),
-                Settings.System.DATE_FORMAT, UserHandle.myUserId());
+        String value = Settings.System.getString(context.getContentResolver(),
+                Settings.System.DATE_FORMAT);
 
         return getDateFormatForSetting(context, value);
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dd1cbc9..4299e2e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6111,6 +6111,33 @@
                     }
                 }
             } break;
+
+
+            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+                if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView != null) {
+                    // We care only for changes rooted in the focused host.
+                    final long eventSourceId = event.getSourceNodeId();
+                    final int hostViewId = AccessibilityNodeInfo.getAccessibilityViewId(
+                            eventSourceId);
+                    if (hostViewId != mAccessibilityFocusedHost.getAccessibilityViewId()) {
+                        break;
+                    }
+
+                    // We only care about changes that may change the virtual focused view bounds.
+                    final int changes = event.getContentChangeTypes();
+                    if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0
+                            || changes == AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
+                        AccessibilityNodeProvider provider = mAccessibilityFocusedHost
+                                .getAccessibilityNodeProvider();
+                        if (provider != null) {
+                            final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId(
+                                    mAccessibilityFocusedVirtualView.getSourceNodeId());
+                            mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(
+                                    virtualChildId);
+                        }
+                    }
+                }
+            } break;
         }
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index b3ff88b..45d1403 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -29,7 +29,6 @@
 import android.provider.Settings;
 import android.widget.TextView;
 import android.widget.RemoteViews.RemoteView;
-import android.os.UserHandle;
 
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
@@ -191,15 +190,8 @@
     }
 
     private DateFormat getDateFormat() {
-        // OK, this is gross but needed. This class is supported by the
-        // remote views mechanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For example, when adding widgets from a user profile to the
-        // home screen. Therefore, we access settings as the user the app
-        // is running as not the one the context is for.
-        String format = Settings.System.getStringForUser(getContext().getContentResolver(),
-                Settings.System.DATE_FORMAT, UserHandle.myUserId());
+        String format = Settings.System.getString(getContext().getContentResolver(),
+                Settings.System.DATE_FORMAT);
         if (format == null || "".equals(format)) {
             return DateFormat.getDateInstance(DateFormat.SHORT);
         } else {
@@ -220,20 +212,10 @@
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
-        // OK, this is gross but needed. This class is supported by the
-        // remote views mechanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For example, when adding widgets from a user profile to the
-        // home screen. Therefore, we register the receiver and content
-        // observer as the user the app is running as not the one the context is for.
-        context.registerReceiverAsUser(mBroadcastReceiver, android.os.Process.myUserHandle(),
-                filter, null, null);
+        context.registerReceiver(mBroadcastReceiver, filter);
 
         Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT);
-        context.getContentResolver().registerContentObserver(uri, true, mContentObserver,
-                UserHandle.myUserId());
+        context.getContentResolver().registerContentObserver(uri, true, mContentObserver);
     }
 
     private void unregisterReceivers() {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9de69f2..69d5f40 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1653,10 +1653,8 @@
      *
      * @param application The application whose content is shown by the views.
      * @param layoutId The id of the layout resource.
-     *
-     * @hide
      */
-    protected RemoteViews(ApplicationInfo application, int layoutId) {
+    private RemoteViews(ApplicationInfo application, int layoutId) {
         mApplication = application;
         mLayoutId = layoutId;
         mBitmapCache = new BitmapCache();
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index acfc543..4c5c71d 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -26,7 +26,6 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.format.DateFormat;
 import android.util.AttributeSet;
@@ -496,28 +495,12 @@
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
 
-        // OK, this is gross but needed. This class is supported by the
-        // remote views mechanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For example, when adding widgets from a user profile to the
-        // home screen. Therefore, we register the receiver as the user
-        // the app is running as not the one the context is for.
-        getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
-                filter, null, getHandler());
+        getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
     }
 
     private void registerObserver() {
         final ContentResolver resolver = getContext().getContentResolver();
-        // OK, this is gross but needed. This class is supported by the
-        // remote views mechanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For example, when adding widgets from a user profile to the
-        // home screen. Therefore, we register the content observer
-        // as the user the app is running as not the one the context is for.
-        resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
-                mFormatChangeObserver, UserHandle.myUserId());
+        resolver.registerContentObserver(Settings.System.CONTENT_URI, true, mFormatChangeObserver);
     }
 
     private void unregisterReceiver() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 188a3e9..3e1b674 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -41,7 +41,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.BoringLayout;
 import android.text.DynamicLayout;
@@ -8338,15 +8337,8 @@
      * to speak passwords.
      */
     private boolean shouldSpeakPasswordsForAccessibility() {
-        // OK, this is gross but needed. This class is supported by the
-        // remote views mechanism and as a part of that the remote views
-        // can be inflated by a context for another user without the app
-        // having interact users permission - just for loading resources.
-        // For example, when adding widgets from a user profile to the
-        // home screen. Therefore, we access settings as user the app is
-        // running as not the one the context is for.
-        return (Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, UserHandle.myUserId()) == 1);
+        return (Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) == 1);
     }
 
     @Override
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 8069d13..edf6d9f 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -46,4 +46,9 @@
         <item>GPS_LOCK=1</item>
         <item>LPP_PROFILE=3</item>
     </string-array>
+    <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+    <string-array name="config_twoDigitNumberPattern">
+        <item>"0"</item>
+        <item>"00"</item>
+    </string-array>
 </resources>
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index cd919a6..43922b8 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -419,18 +419,22 @@
     private void endSoftwareAnimations() {
         if (mAnimRadius != null) {
             mAnimRadius.end();
+            mAnimRadius = null;
         }
 
         if (mAnimOpacity != null) {
             mAnimOpacity.end();
+            mAnimOpacity = null;
         }
 
         if (mAnimX != null) {
             mAnimX.end();
+            mAnimX = null;
         }
 
         if (mAnimY != null) {
             mAnimY.end();
+            mAnimY = null;
         }
     }
 
@@ -506,18 +510,22 @@
     private void cancelSoftwareAnimations() {
         if (mAnimRadius != null) {
             mAnimRadius.cancel();
+            mAnimRadius = null;
         }
 
         if (mAnimOpacity != null) {
             mAnimOpacity.cancel();
+            mAnimOpacity = null;
         }
 
         if (mAnimX != null) {
             mAnimX.cancel();
+            mAnimX = null;
         }
 
         if (mAnimY != null) {
             mAnimY.cancel();
+            mAnimY = null;
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 4e68a60..80ecea3 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -326,6 +326,7 @@
     private void endSoftwareAnimations() {
         if (mAnimOuterOpacity != null) {
             mAnimOuterOpacity.end();
+            mAnimOuterOpacity = null;
         }
     }
 
@@ -413,6 +414,7 @@
     private void cancelSoftwareAnimations() {
         if (mAnimOuterOpacity != null) {
             mAnimOuterOpacity.cancel();
+            mAnimOuterOpacity = null;
         }
     }
 
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 6e1402c..c4a55d1 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -23,6 +23,7 @@
 
 LOCAL_PACKAGE_NAME := PrintSpooler
 
+LOCAL_JNI_SHARED_LIBRARIES := libprintspooler_jni
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
 
 include $(BUILD_PACKAGE)
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7f7e5c3..2ef806f 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1863,7 +1863,7 @@
 
     private void deleteProviderLocked(Provider provider) {
         int N = provider.widgets.size();
-        for (int i = 0; i < N; i++) {
+        for (int i = N - 1; i >= 0; i--) {
             Widget widget = provider.widgets.remove(i);
             // Call back with empty RemoteViews
             updateAppWidgetInstanceLocked(widget, null, false);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b116d76..023f627 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3215,7 +3215,6 @@
         if (resumed) {
             if (mUsageStatsService != null) {
                 mUsageStatsService.reportEvent(component.realActivity, component.userId,
-                        System.currentTimeMillis(),
                         UsageEvents.Event.MOVE_TO_FOREGROUND);
             }
             synchronized (stats) {
@@ -3224,7 +3223,6 @@
         } else {
             if (mUsageStatsService != null) {
                 mUsageStatsService.reportEvent(component.realActivity, component.userId,
-                        System.currentTimeMillis(),
                         UsageEvents.Event.MOVE_TO_BACKGROUND);
             }
             synchronized (stats) {
@@ -15936,6 +15934,7 @@
                 newConfig.seq = mConfigurationSeq;
                 mConfiguration = newConfig;
                 Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
+                mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
                 //mUsageStatsService.noteStartConfig(newConfig);
 
                 final Configuration configCopy = new Configuration(mConfiguration);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 228c120..846efc0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10245,8 +10245,18 @@
                 if (bp != null) {
                     // If the defining package is signed with our cert, it's okay.  This
                     // also includes the "updating the same package" case, of course.
-                    if (compareSignatures(bp.packageSetting.signatures.mSignatures,
-                            pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+                    // "updating same package" could also involve key-rotation.
+                    final boolean sigsOk;
+                    if (!bp.sourcePackage.equals(pkg.packageName)
+                            || !(bp.packageSetting instanceof PackageSetting)
+                            || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()
+                            || ((PackageSetting) bp.packageSetting).sharedUser != null) {
+                        sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
+                                pkg.mSignatures) != PackageManager.SIGNATURE_MATCH;
+                    } else {
+                        sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
+                    }
+                    if (!sigsOk) {
                         // If the owning package is the system itself, we log but allow
                         // install to proceed; we fail the install on all other permission
                         // redefinitions.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cad2772..3e6191c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2996,10 +2996,12 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
-        synchronized (this) {
-            DevicePolicyData policy = getUserData(userHandle);
-            long ident = Binder.clearCallingIdentity();
-            try {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            boolean wipeData = false;
+            int identifier = 0;
+            synchronized (this) {
+                DevicePolicyData policy = getUserData(userHandle);
                 policy.mFailedPasswordAttempts++;
                 saveSettingsLocked(userHandle);
                 if (mHasFeature) {
@@ -3011,15 +3013,20 @@
                         // Wipe the user/profile associated with the policy that was violated. This
                         // is not necessarily calling user: if the policy that fired was from a
                         // managed profile rather than the main user profile, we wipe former only.
-                        wipeDeviceOrUserLocked(0, strictestAdmin.getUserHandle().getIdentifier());
+                        wipeData = true;
+                        identifier = strictestAdmin.getUserHandle().getIdentifier();
                     }
                     sendAdminCommandToSelfAndProfilesLocked(
                             DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
                             DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
                 }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
             }
+            if (wipeData) {
+                // Call without holding lock.
+                wipeDeviceOrUserLocked(0, identifier);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index dc036e2..6c80a65 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,17 +15,23 @@
  */
 package com.android.server.usage;
 
+import android.app.usage.ConfigurationStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
+import android.content.res.Configuration;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import java.util.ArrayList;
+
 class IntervalStats {
     public long beginTime;
     public long endTime;
     public long lastTimeSaved;
     public final ArrayMap<String, UsageStats> stats = new ArrayMap<>();
+    public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
+    public Configuration activeConfiguration;
     public TimeSparseArray<UsageEvents.Event> events;
 
     // A string cache. This is important as when we're parsing XML files, we don't want to
@@ -34,18 +40,49 @@
     // strings that had identical copies in the cache.
     private final ArraySet<String> mStringCache = new ArraySet<>();
 
+    /**
+     * Gets the UsageStats object for the given package, or creates one and adds it internally.
+     */
     UsageStats getOrCreateUsageStats(String packageName) {
         UsageStats usageStats = stats.get(packageName);
         if (usageStats == null) {
             usageStats = new UsageStats();
-            usageStats.mPackageName = packageName;
+            usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
-            stats.put(packageName, usageStats);
+            stats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
     }
 
+    /**
+     * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
+     * internally.
+     */
+    ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
+        ConfigurationStats configStats = configurations.get(config);
+        if (configStats == null) {
+            configStats = new ConfigurationStats();
+            configStats.mBeginTimeStamp = beginTime;
+            configStats.mEndTimeStamp = endTime;
+            configStats.mConfiguration = config;
+            configurations.put(config, configStats);
+        }
+        return configStats;
+    }
+
+    /**
+     * Builds a UsageEvents.Event, but does not add it internally.
+     */
+    UsageEvents.Event buildEvent(String packageName, String className) {
+        UsageEvents.Event event = new UsageEvents.Event();
+        event.mPackage = getCachedStringRef(packageName);
+        if (className != null) {
+            event.mClass = getCachedStringRef(className);
+        }
+        return event;
+    }
+
     void update(String packageName, long timeStamp, int eventType) {
         UsageStats usageStats = getOrCreateUsageStats(packageName);
 
@@ -61,6 +98,28 @@
         usageStats.mLastEvent = eventType;
         usageStats.mLastTimeUsed = timeStamp;
         usageStats.mEndTimeStamp = timeStamp;
+
+        if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+            usageStats.mLaunchCount += 1;
+        }
+
+        endTime = timeStamp;
+    }
+
+    void updateConfigurationStats(Configuration config, long timeStamp) {
+        if (activeConfiguration != null) {
+            ConfigurationStats activeStats = configurations.get(activeConfiguration);
+            activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
+            activeStats.mLastTimeActive = timeStamp - 1;
+        }
+
+        if (config != null) {
+            ConfigurationStats configStats = getOrCreateConfigurationStats(config);
+            configStats.mLastTimeActive = timeStamp;
+            configStats.mActivationCount += 1;
+            activeConfiguration = configStats.mConfiguration;
+        }
+
         endTime = timeStamp;
     }
 
@@ -72,13 +131,4 @@
         }
         return mStringCache.valueAt(index);
     }
-
-    UsageEvents.Event buildEvent(String packageName, String className) {
-        UsageEvents.Event event = new UsageEvents.Event();
-        event.mPackage = getCachedStringRef(packageName);
-        if (className != null) {
-            event.mClass = getCachedStringRef(className);
-        }
-        return event;
-    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index e6ce0fe..37340a4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,7 +17,6 @@
 package com.android.server.usage;
 
 import android.app.usage.TimeSparseArray;
-import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -133,9 +132,30 @@
     }
 
     /**
-     * Find all {@link UsageStats} for the given range and interval type.
+     * Figures out what to extract from the given IntervalStats object.
      */
-    public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
+    interface StatCombiner<T> {
+
+        /**
+         * Implementations should extract interesting from <code>stats</code> and add it
+         * to the <code>accumulatedResult</code> list.
+         *
+         * If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
+         * which means you should make a copy of the data before adding it to the
+         * <code>accumulatedResult</code> list.
+         *
+         * @param stats The {@link IntervalStats} object selected.
+         * @param mutable Whether or not the data inside the stats object is mutable.
+         * @param accumulatedResult The list to which to add extracted data.
+         */
+        void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
+    }
+
+    /**
+     * Find all {@link IntervalStats} for the given range and interval type.
+     */
+    public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
+            StatCombiner<T> combiner) {
         synchronized (mLock) {
             if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
                 throw new IllegalArgumentException("Bad interval type " + intervalType);
@@ -157,7 +177,7 @@
 
             try {
                 IntervalStats stats = new IntervalStats();
-                ArrayList<UsageStats> results = new ArrayList<>();
+                ArrayList<T> results = new ArrayList<>();
                 for (int i = startIndex; i <= endIndex; i++) {
                     final AtomicFile f = mSortedStatFiles[intervalType].valueAt(i);
 
@@ -167,7 +187,7 @@
 
                     UsageStatsXml.read(f, stats);
                     if (beginTime < stats.endTime) {
-                        results.addAll(stats.stats.values());
+                        combiner.combine(stats, false, results);
                     }
                 }
                 return results;
@@ -209,6 +229,10 @@
     public void prune() {
         synchronized (mLock) {
             long timeNow = System.currentTimeMillis();
+            mCal.setTimeInMillis(timeNow);
+            mCal.add(Calendar.YEAR, -3);
+            pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
+                    mCal.getTimeInMillis());
 
             mCal.setTimeInMillis(timeNow);
             mCal.add(Calendar.MONTH, -6);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0e8b427..e77bf86 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.app.AppOpsManager;
+import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
@@ -30,11 +31,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArraySet;
@@ -218,8 +221,7 @@
      * Called by the Binder stub.
      */
     List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) {
-        final long timeNow = System.currentTimeMillis();
-        if (beginTime > timeNow) {
+        if (!validRange(beginTime, endTime)) {
             return null;
         }
 
@@ -232,15 +234,23 @@
     /**
      * Called by the Binder stub.
      */
+    List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
+            long endTime) {
+        if (!validRange(beginTime, endTime)) {
+            return null;
+        }
+
+        synchronized (mLock) {
+            UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId);
+            return service.queryConfigurationStats(bucketType, beginTime, endTime);
+        }
+    }
+
+    /**
+     * Called by the Binder stub.
+     */
     UsageEvents queryEvents(int userId, long beginTime, long endTime) {
-        final long timeNow = System.currentTimeMillis();
-
-        // Adjust the endTime so that we don't query for the latest events.
-        // This is to prevent apps from making decision based on what app launched them,
-        // etc.
-        endTime = Math.min(endTime, timeNow - END_TIME_DELAY);
-
-        if (beginTime > endTime) {
+        if (!validRange(beginTime, endTime)) {
             return null;
         }
 
@@ -250,6 +260,11 @@
         }
     }
 
+    private static boolean validRange(long beginTime, long endTime) {
+        final long timeNow = System.currentTimeMillis();
+        return beginTime <= timeNow && beginTime < endTime;
+    }
+
     private void flushToDiskLocked() {
         final int userCount = mUserState.size();
         for (int i = 0; i < userCount; i++) {
@@ -323,6 +338,28 @@
         }
 
         @Override
+        public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
+                long beginTime, long endTime, String callingPackage) throws RemoteException {
+            if (!hasPermission(callingPackage)) {
+                return null;
+            }
+
+            final int userId = UserHandle.getCallingUserId();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final List<ConfigurationStats> results =
+                        UsageStatsService.this.queryConfigurationStats(userId, bucketType,
+                                beginTime, endTime);
+                if (results != null) {
+                    return new ParceledListSlice<>(results);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return null;
+        }
+
+        @Override
         public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
             if (!hasPermission(callingPackage)) {
                 return null;
@@ -346,8 +383,7 @@
     private class LocalService extends UsageStatsManagerInternal {
 
         @Override
-        public void reportEvent(ComponentName component, int userId,
-                long timeStamp, int eventType) {
+        public void reportEvent(ComponentName component, int userId, int eventType) {
             if (component == null) {
                 Slog.w(TAG, "Event reported without a component name");
                 return;
@@ -356,12 +392,27 @@
             UsageEvents.Event event = new UsageEvents.Event();
             event.mPackage = component.getPackageName();
             event.mClass = component.getClassName();
-            event.mTimeStamp = timeStamp;
+            event.mTimeStamp = System.currentTimeMillis();
             event.mEventType = eventType;
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
 
         @Override
+        public void reportConfigurationChange(Configuration config, int userId) {
+            if (config == null) {
+                Slog.w(TAG, "Configuration event reported with a null config");
+                return;
+            }
+
+            UsageEvents.Event event = new UsageEvents.Event();
+            event.mPackage = "android";
+            event.mTimeStamp = System.currentTimeMillis();
+            event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
+            event.mConfiguration = new Configuration(config);
+            mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+        }
+
+        @Override
         public void prepareShutdown() {
             // This method *WILL* do IO work, but we must block until it is finished or else
             // we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 374429a..6529950 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,68 +20,69 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
+import android.app.usage.ConfigurationStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.ComponentName;
+import android.content.res.Configuration;
 
 import java.io.IOException;
 import java.net.ProtocolException;
+import java.util.Locale;
 
 /**
  * UsageStats reader/writer for version 1 of the XML format.
  */
 final class UsageStatsXmlV1 {
+    private static final String PACKAGE_TAG = "package";
+    private static final String CONFIGURATION_TAG = "config";
+    private static final String EVENT_LOG_TAG = "event-log";
+
     private static final String BEGIN_TIME_ATTR = "beginTime";
     private static final String END_TIME_ATTR = "endTime";
-    private static final String PACKAGE_TAG = "package";
     private static final String NAME_ATTR = "name";
     private static final String PACKAGE_ATTR = "package";
     private static final String CLASS_ATTR = "class";
     private static final String TOTAL_TIME_ACTIVE_ATTR = "totalTimeActive";
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+    private static final String COUNT_ATTR = "count";
+    private static final String ACTIVE_ATTR = "active";
     private static final String LAST_EVENT_ATTR = "lastEvent";
-    private static final String EVENT_LOG_TAG = "event-log";
     private static final String TYPE_ATTR = "type";
     private static final String TIME_ATTR = "time";
 
-    private static UsageStats readNextUsageStats(XmlPullParser parser)
+    private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
             throws XmlPullParserException, IOException {
-        if (parser.getEventType() != XmlPullParser.START_TAG) {
-            XmlUtils.nextElement(parser);
-        }
-
-        if (parser.getEventType() != XmlPullParser.START_TAG ||
-                !parser.getName().equals(PACKAGE_TAG)) {
-            return null;
-        }
-
         final String name = parser.getAttributeValue(null, NAME_ATTR);
         if (name == null) {
             throw new ProtocolException("no " + NAME_ATTR + " attribute present");
         }
 
-        UsageStats stats = new UsageStats();
-        stats.mPackageName = name;
+        UsageStats stats = statsOut.getOrCreateUsageStats(name);
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastTimeUsed = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
-        XmlUtils.skipCurrentTag(parser);
-        return stats;
     }
 
-    private static UsageEvents.Event readNextEvent(XmlPullParser parser, IntervalStats statsOut)
+    private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
             throws XmlPullParserException, IOException {
-        if (parser.getEventType() != XmlPullParser.START_TAG) {
-            XmlUtils.nextElement(parser);
-        }
+        final Configuration config = new Configuration();
+        Configuration.readXmlAttrs(parser, config);
 
-        if (parser.getEventType() != XmlPullParser.START_TAG ||
-                !parser.getName().equals(EVENT_LOG_TAG)) {
-            return null;
+        ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
+        configStats.mLastTimeActive = XmlUtils.readLongAttribute(parser, LAST_TIME_ACTIVE_ATTR);
+        configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
+        configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
+        if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
+            statsOut.activeConfiguration = configStats.mConfiguration;
         }
+    }
 
+    private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
+            throws XmlPullParserException, IOException {
         String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
         String className;
         if (packageName == null) {
@@ -105,31 +106,60 @@
         UsageEvents.Event event = statsOut.buildEvent(packageName, className);
         event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
         event.mTimeStamp = XmlUtils.readLongAttribute(parser, TIME_ATTR);
-        XmlUtils.skipCurrentTag(parser);
-        return event;
-    }
 
-    private static void writeUsageStats(FastXmlSerializer serializer, UsageStats stats)
-            throws IOException {
-        serializer.startTag(null, PACKAGE_TAG);
-        serializer.attribute(null, NAME_ATTR, stats.mPackageName);
-        serializer.attribute(null, TOTAL_TIME_ACTIVE_ATTR,
-                Long.toString(stats.mTotalTimeInForeground));
-        serializer.attribute(null, LAST_TIME_ACTIVE_ATTR, Long.toString(stats.mLastTimeUsed));
-        serializer.attribute(null, LAST_EVENT_ATTR, Integer.toString(stats.mLastEvent));
-        serializer.endTag(null, PACKAGE_TAG);
-    }
-
-    private static void writeEvent(FastXmlSerializer serializer, UsageEvents.Event event)
-            throws IOException {
-        serializer.startTag(null, EVENT_LOG_TAG);
-        serializer.attribute(null, PACKAGE_ATTR, event.mPackage);
-        if (event.mClass != null) {
-            serializer.attribute(null, CLASS_ATTR, event.mClass);
+        if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+            event.mConfiguration = new Configuration();
+            Configuration.readXmlAttrs(parser, event.mConfiguration);
         }
-        serializer.attribute(null, TYPE_ATTR, Integer.toString(event.getEventType()));
-        serializer.attribute(null, TIME_ATTR, Long.toString(event.getTimeStamp()));
-        serializer.endTag(null, EVENT_LOG_TAG);
+
+        if (statsOut.events == null) {
+            statsOut.events = new TimeSparseArray<>();
+        }
+        statsOut.events.put(event.mTimeStamp, event);
+    }
+
+    private static void writeUsageStats(XmlSerializer xml, final UsageStats stats)
+            throws IOException {
+        xml.startTag(null, PACKAGE_TAG);
+        XmlUtils.writeStringAttribute(xml, NAME_ATTR, stats.mPackageName);
+        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeInForeground);
+        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeUsed);
+        XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, stats.mLastEvent);
+        xml.endTag(null, PACKAGE_TAG);
+    }
+
+    private static void writeConfigStats(XmlSerializer xml, final ConfigurationStats stats,
+            boolean isActive) throws IOException {
+        xml.startTag(null, CONFIGURATION_TAG);
+        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, stats.mLastTimeActive);
+        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, stats.mTotalTimeActive);
+        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, stats.mActivationCount);
+        if (isActive) {
+            XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
+        }
+
+        // Now write the attributes representing the configuration object.
+        Configuration.writeXmlAttrs(xml, stats.mConfiguration);
+
+        xml.endTag(null, CONFIGURATION_TAG);
+    }
+
+    private static void writeEvent(XmlSerializer xml, final UsageEvents.Event event)
+            throws IOException {
+        xml.startTag(null, EVENT_LOG_TAG);
+        XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
+        if (event.mClass != null) {
+            XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
+        }
+        XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+        XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp);
+
+        if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE
+                && event.mConfiguration != null) {
+            Configuration.writeXmlAttrs(xml, event.mConfiguration);
+        }
+
+        xml.endTag(null, EVENT_LOG_TAG);
     }
 
     /**
@@ -142,6 +172,8 @@
     public static void read(XmlPullParser parser, IntervalStats statsOut)
             throws XmlPullParserException, IOException {
         statsOut.stats.clear();
+        statsOut.configurations.clear();
+        statsOut.activeConfiguration = null;
 
         if (statsOut.events != null) {
             statsOut.events.clear();
@@ -149,21 +181,29 @@
 
         statsOut.beginTime = XmlUtils.readLongAttribute(parser, BEGIN_TIME_ATTR);
         statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
-        XmlUtils.nextElement(parser);
 
-        UsageStats pkgStats;
-        while ((pkgStats = readNextUsageStats(parser)) != null) {
-            pkgStats.mBeginTimeStamp = statsOut.beginTime;
-            pkgStats.mEndTimeStamp = statsOut.endTime;
-            statsOut.stats.put(pkgStats.mPackageName, pkgStats);
-        }
-
-        UsageEvents.Event event;
-        while ((event = readNextEvent(parser, statsOut)) != null) {
-            if (statsOut.events == null) {
-                statsOut.events = new TimeSparseArray<>();
+        int eventCode;
+        int outerDepth = parser.getDepth();
+        while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (eventCode != XmlPullParser.START_TAG) {
+                continue;
             }
-            statsOut.events.put(event.getTimeStamp(), event);
+
+            final String tag = parser.getName();
+            switch (tag) {
+                case PACKAGE_TAG:
+                    loadUsageStats(parser, statsOut);
+                    break;
+
+                case CONFIGURATION_TAG:
+                    loadConfigStats(parser, statsOut);
+                    break;
+
+                case EVENT_LOG_TAG:
+                    loadEvent(parser, statsOut);
+                    break;
+            }
         }
     }
 
@@ -184,11 +224,15 @@
             writeUsageStats(serializer, stats.stats.valueAt(i));
         }
 
-        if (stats.events != null) {
-            final int eventCount = stats.events.size();
-            for (int i = 0; i < eventCount; i++) {
-                writeEvent(serializer, stats.events.valueAt(i));
-            }
+        final int configCount = stats.configurations.size();
+        for (int i = 0; i < configCount; i++) {
+            boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+            writeConfigStats(serializer, stats.configurations.valueAt(i), active);
+        }
+
+        final int eventCount = stats.events != null ? stats.events.size() : 0;
+        for (int i = 0; i < eventCount; i++) {
+            writeEvent(serializer, stats.events.valueAt(i));
         }
     }
 
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 6951590..7142a99 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,13 +16,17 @@
 
 package com.android.server.usage;
 
+import android.app.usage.ConfigurationStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
+import android.content.res.Configuration;
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+
 import java.io.File;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
@@ -108,35 +112,91 @@
                     notifyStatsChanged();
                 }
             }
+
+            stat.updateConfigurationStats(null, stat.lastTimeSaved);
         }
     }
 
     void reportEvent(UsageEvents.Event event) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
-                    + "[" + event.getTimeStamp() + "]: "
-                    + eventToString(event.getEventType()));
+                    + "[" + event.mTimeStamp + "]: "
+                    + eventToString(event.mEventType));
         }
 
-        if (event.getTimeStamp() >= mDailyExpiryDate.getTimeInMillis()) {
+        if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
             rolloverStats();
         }
 
-        if (mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events == null) {
-            mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events = new TimeSparseArray<>();
+        final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+
+        final Configuration newFullConfig = event.mConfiguration;
+        if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
+                currentDailyStats.activeConfiguration != null) {
+            // Make the event configuration a delta.
+            event.mConfiguration = Configuration.generateDelta(
+                    currentDailyStats.activeConfiguration, newFullConfig);
         }
-        mCurrentStats[UsageStatsManager.INTERVAL_DAILY].events.put(event.getTimeStamp(), event);
+
+        // Add the event to the daily list.
+        if (currentDailyStats.events == null) {
+            currentDailyStats.events = new TimeSparseArray<>();
+        }
+        currentDailyStats.events.put(event.mTimeStamp, event);
 
         for (IntervalStats stats : mCurrentStats) {
-            stats.update(event.mPackage, event.getTimeStamp(),
-                    event.getEventType());
+            if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+                stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+            } else {
+                stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+            }
         }
 
         notifyStatsChanged();
     }
 
-    List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+    private static final StatCombiner<UsageStats> sUsageStatsCombiner =
+            new StatCombiner<UsageStats>() {
+                @Override
+                public void combine(IntervalStats stats, boolean mutable,
+                        List<UsageStats> accResult) {
+                    if (!mutable) {
+                        accResult.addAll(stats.stats.values());
+                        return;
+                    }
+
+                    final int statCount = stats.stats.size();
+                    for (int i = 0; i < statCount; i++) {
+                        accResult.add(new UsageStats(stats.stats.valueAt(i)));
+                    }
+                }
+            };
+
+    private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
+            new StatCombiner<ConfigurationStats>() {
+                @Override
+                public void combine(IntervalStats stats, boolean mutable,
+                        List<ConfigurationStats> accResult) {
+                    if (!mutable) {
+                        accResult.addAll(stats.configurations.values());
+                        return;
+                    }
+
+                    final int configCount = stats.configurations.size();
+                    for (int i = 0; i < configCount; i++) {
+                        accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
+                    }
+                }
+            };
+
+    /**
+     * Generic query method that selects the appropriate IntervalStats for the specified time range
+     * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
+     * provided to select the stats to use from the IntervalStats object.
+     */
+    private <T> List<T> queryStats(int bucketType, long beginTime, long endTime,
+            StatCombiner<T> combiner) {
         if (bucketType == UsageStatsManager.INTERVAL_BEST) {
             bucketType = mDatabase.findBestFitBucket(beginTime, endTime);
         }
@@ -161,11 +221,8 @@
                 Slog.d(TAG, mLogPrefix + "Returning in-memory stats for bucket " + bucketType);
             }
             // Fast path for retrieving in-memory state.
-            ArrayList<UsageStats> results = new ArrayList<>();
-            final int packageCount = mCurrentStats[bucketType].stats.size();
-            for (int i = 0; i < packageCount; i++) {
-                results.add(new UsageStats(mCurrentStats[bucketType].stats.valueAt(i)));
-            }
+            ArrayList<T> results = new ArrayList<>();
+            combiner.combine(mCurrentStats[bucketType], true, results);
             return results;
         }
 
@@ -178,13 +235,21 @@
                     + beginTime + " AND endTime < " + endTime);
         }
 
-        final List<UsageStats> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime);
+        final List<T> results = mDatabase.queryUsageStats(bucketType, beginTime, endTime, combiner);
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Results: " + (results == null ? 0 : results.size()));
         }
         return results;
     }
 
+    List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+        return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
+    }
+
+    List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
+        return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
+    }
+
     UsageEvents queryEvents(long beginTime, long endTime) {
         if (endTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime) {
             if (beginTime > mCurrentStats[UsageStatsManager.INTERVAL_DAILY].endTime) {
@@ -245,6 +310,8 @@
 
         // Finish any ongoing events with an END_OF_DAY event. Make a note of which components
         // need a new CONTINUE_PREVIOUS_DAY entry.
+        final Configuration previousConfig =
+                mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
         ArraySet<String> continuePreviousDay = new ArraySet<>();
         for (IntervalStats stat : mCurrentStats) {
             final int pkgCount = stat.stats.size();
@@ -253,11 +320,13 @@
                 if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
                         pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
                     continuePreviousDay.add(pkgStats.mPackageName);
-                    stat.update(pkgStats.mPackageName,
-                            mDailyExpiryDate.getTimeInMillis() - 1, UsageEvents.Event.END_OF_DAY);
+                    stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
+                            UsageEvents.Event.END_OF_DAY);
                     mStatsChanged = true;
                 }
             }
+
+            stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
         }
 
         persistActiveStats();
@@ -267,9 +336,10 @@
         final int continueCount = continuePreviousDay.size();
         for (int i = 0; i < continueCount; i++) {
             String name = continuePreviousDay.valueAt(i);
+            final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
             for (IntervalStats stat : mCurrentStats) {
-                stat.update(name, mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime,
-                        UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+                stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+                stat.updateConfigurationStats(previousConfig, beginTime);
                 mStatsChanged = true;
             }
         }
@@ -353,6 +423,8 @@
                 return "END_OF_DAY";
             case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
                 return "CONTINUE_PREVIOUS_DAY";
+            case UsageEvents.Event.CONFIGURATION_CHANGE:
+                return "CONFIGURATION_CHANGE";
             default:
                 return "UNKNOWN";
         }
diff --git a/telecomm/java/android/telecomm/Log.java b/telecomm/java/android/telecomm/Log.java
index b8dfb11..446ae75 100644
--- a/telecomm/java/android/telecomm/Log.java
+++ b/telecomm/java/android/telecomm/Log.java
@@ -31,7 +31,7 @@
     // Generic tag for all Telecomm Framework logging
     private static final String TAG = "TelecommFramework";
 
-    public static final boolean FORCE_LOGGING = true; /* STOP SHIP if true */
+    public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
     public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
     public static final boolean INFO = isLoggable(android.util.Log.INFO);
     public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index acb97a0..8af5e98 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -164,14 +164,6 @@
             return uri.getSchemeSpecificPart();
         }
 
-        // TODO: We don't check for SecurityException here (requires
-        // CALL_PRIVILEGED permission).
-        if (scheme.equals("voicemail")) {
-            long subId = intent.getLongExtra(SUBSCRIPTION_KEY,
-                    SubscriptionManager.getDefaultVoiceSubId());
-            return TelephonyManager.getDefault().getCompleteVoiceMailNumber(subId);
-        }
-
         if (context == null) {
             return null;
         }
diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk
index 69fefeb..5f7467a 100644
--- a/tests/UsageStatsTest/Android.mk
+++ b/tests/UsageStatsTest/Android.mk
@@ -6,6 +6,8 @@
 # Only compile source java files in this apk.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+
 LOCAL_PACKAGE_NAME := UsageStatsTest
 
 include $(BUILD_PACKAGE)
diff --git a/tests/UsageStatsTest/res/layout/config_row_item.xml b/tests/UsageStatsTest/res/layout/config_row_item.xml
new file mode 100644
index 0000000..547de04
--- /dev/null
+++ b/tests/UsageStatsTest/res/layout/config_row_item.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp"/>
diff --git a/tests/UsageStatsTest/res/layout/row_item.xml b/tests/UsageStatsTest/res/layout/row_item.xml
index da50163..4f2bfe4 100644
--- a/tests/UsageStatsTest/res/layout/row_item.xml
+++ b/tests/UsageStatsTest/res/layout/row_item.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingLeft="16dp"
@@ -8,13 +9,10 @@
     <TextView android:id="@android:id/text1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_centerVertical="true"
+        android:layout_weight="1"
         android:textStyle="bold"/>
 
     <TextView android:id="@android:id/text2"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_alignBaseline="@android:id/text1"/>
-</RelativeLayout>
+        android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
index 5f62ad8..31e7c38 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageLogActivity.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.support.v4.util.CircularArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,11 +36,14 @@
     private UsageStatsManager mUsageStatsManager;
     private Adapter mAdapter;
     private Handler mHandler = new Handler();
+    private long mLastTime;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+        mLastTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
+
         mAdapter = new Adapter();
         setListAdapter(mAdapter);
     }
@@ -59,24 +63,31 @@
     @Override
     public void run() {
         long now = System.currentTimeMillis();
-        long beginTime = now - USAGE_STATS_PERIOD;
-        UsageEvents events = mUsageStatsManager.queryEvents(beginTime, now);
-        mAdapter.update(events);
+        UsageEvents events = mUsageStatsManager.queryEvents(mLastTime, now);
+        long lastEventTime = mAdapter.update(events);
+        if (lastEventTime >= 0) {
+            mLastTime = lastEventTime + 1;
+        }
         mHandler.postDelayed(this, 1000 * 5);
     }
 
     private class Adapter extends BaseAdapter {
+        private static final int MAX_EVENTS = 50;
+        private final CircularArray<UsageEvents.Event> mEvents = new CircularArray<>(MAX_EVENTS);
 
-        private final ArrayList<UsageEvents.Event> mEvents = new ArrayList<>();
-
-        public void update(UsageEvents results) {
-            mEvents.clear();
+        public long update(UsageEvents results) {
+            long lastTimeStamp = -1;
             while (results.hasNextEvent()) {
                 UsageEvents.Event event = new UsageEvents.Event();
                 results.getNextEvent(event);
-                mEvents.add(event);
+                lastTimeStamp = event.getTimeStamp();
+                if (mEvents.size() == MAX_EVENTS) {
+                    mEvents.popLast();
+                }
+                mEvents.addFirst(event);
             }
             notifyDataSetChanged();
+            return lastTimeStamp;
         }
 
         @Override
@@ -85,7 +96,7 @@
         }
 
         @Override
-        public Object getItem(int position) {
+        public UsageEvents.Event getItem(int position) {
             return mEvents.get(position);
         }
 
@@ -95,41 +106,72 @@
         }
 
         @Override
+        public int getItemViewType(int position) {
+            final int eventType = getItem(position).getEventType();
+            if (eventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
+                return 1;
+            }
+            return 0;
+        }
+
+        @Override
         public View getView(int position, View convertView, ViewGroup parent) {
+            final UsageEvents.Event event = getItem(position);
+
             final ViewHolder holder;
             if (convertView == null) {
-                convertView = LayoutInflater.from(UsageLogActivity.this)
-                        .inflate(R.layout.row_item, parent, false);
                 holder = new ViewHolder();
-                holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
-                holder.state = (TextView) convertView.findViewById(android.R.id.text2);
+
+                if (event.getEventType() == UsageEvents.Event.CONFIGURATION_CHANGE) {
+                    convertView = LayoutInflater.from(UsageLogActivity.this)
+                            .inflate(R.layout.config_row_item, parent, false);
+                    holder.config = (TextView) convertView.findViewById(android.R.id.text1);
+                } else {
+                    convertView = LayoutInflater.from(UsageLogActivity.this)
+                            .inflate(R.layout.row_item, parent, false);
+                    holder.packageName = (TextView) convertView.findViewById(android.R.id.text1);
+                    holder.state = (TextView) convertView.findViewById(android.R.id.text2);
+                }
                 convertView.setTag(holder);
             } else {
                 holder = (ViewHolder) convertView.getTag();
             }
 
-            holder.packageName.setText(mEvents.get(position).getPackageName());
-            String state;
-            switch (mEvents.get(position).getEventType()) {
+            if (holder.packageName != null) {
+                holder.packageName.setText(event.getPackageName());
+            }
+
+            if (holder.state != null) {
+                holder.state.setText(eventToString(event.getEventType()));
+            }
+
+            if (holder.config != null &&
+                    event.getEventType() == UsageEvents.Event.CONFIGURATION_CHANGE) {
+                holder.config.setText(event.getConfiguration().toString());
+            }
+            return convertView;
+        }
+
+        private String eventToString(int eventType) {
+            switch (eventType) {
                 case UsageEvents.Event.MOVE_TO_FOREGROUND:
-                    state = "Foreground";
-                    break;
+                    return "Foreground";
 
                 case UsageEvents.Event.MOVE_TO_BACKGROUND:
-                    state = "Background";
-                    break;
+                    return "Background";
+
+                case UsageEvents.Event.CONFIGURATION_CHANGE:
+                    return "Config change";
 
                 default:
-                    state = "Unknown: " + mEvents.get(position).getEventType();
-                    break;
+                    return "Unknown: " + eventType;
             }
-            holder.state.setText(state);
-            return convertView;
         }
     }
 
     static class ViewHolder {
         public TextView packageName;
         public TextView state;
+        public TextView config;
     }
 }