Merge "Do not reset language in case of the TTS service reconnect."
diff --git a/Android.mk b/Android.mk
index d279704..45e9c76 100644
--- a/Android.mk
+++ b/Android.mk
@@ -136,6 +136,8 @@
 	core/java/android/hardware/ISerialManager.aidl \
 	core/java/android/hardware/display/IDisplayManager.aidl \
 	core/java/android/hardware/display/IDisplayManagerCallback.aidl \
+	core/java/android/hardware/hdmi/IHdmiCecListener.aidl \
+	core/java/android/hardware/hdmi/IHdmiCecService.aidl \
 	core/java/android/hardware/input/IInputManager.aidl \
 	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
 	core/java/android/hardware/location/IFusedLocationHardware.aidl \
diff --git a/api/current.txt b/api/current.txt
index 09102fb..4c1f6fa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3156,6 +3156,8 @@
     method public final void setProgressBarIndeterminate(boolean);
     method public final void setProgressBarIndeterminateVisibility(boolean);
     method public final void setProgressBarVisibility(boolean);
+    method public void setRecentsIcon(android.graphics.Bitmap);
+    method public void setRecentsLabel(java.lang.CharSequence);
     method public void setRequestedOrientation(int);
     method public final void setResult(int);
     method public final void setResult(int, android.content.Intent);
@@ -3276,6 +3278,8 @@
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public android.graphics.Bitmap activityIcon;
+    field public java.lang.CharSequence activityLabel;
     field public android.content.Intent baseIntent;
     field public java.lang.CharSequence description;
     field public int id;
@@ -4202,6 +4206,20 @@
     method public int describeContents();
     method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int DEFAULT_ALL = -1; // 0xffffffff
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
@@ -4246,6 +4264,7 @@
     field public android.app.Notification.Action[] actions;
     field public int audioStreamType;
     field public android.widget.RemoteViews bigContentView;
+    field public java.lang.String category;
     field public android.app.PendingIntent contentIntent;
     field public android.widget.RemoteViews contentView;
     field public int defaults;
@@ -4306,6 +4325,7 @@
     method public android.os.Bundle getExtras();
     method public deprecated android.app.Notification getNotification();
     method public android.app.Notification.Builder setAutoCancel(boolean);
+    method public android.app.Notification.Builder setCategory(java.lang.String);
     method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -11781,6 +11801,54 @@
 
 }
 
+package android.hardware.hdmi {
+
+  public final class HdmiCec {
+    method public static java.lang.String getDefaultDeviceName(int);
+    method public static int getTypeFromAddress(int);
+    method public static boolean isValidAddress(int);
+    method public static boolean isValidType(int);
+    field public static final int ADDR_AUDIO_SYSTEM = 5; // 0x5
+    field public static final int ADDR_BROADCAST = 15; // 0xf
+    field public static final int ADDR_FREE_USE = 14; // 0xe
+    field public static final int ADDR_INVALID = -1; // 0xffffffff
+    field public static final int ADDR_PLAYBACK_1 = 4; // 0x4
+    field public static final int ADDR_PLAYBACK_2 = 8; // 0x8
+    field public static final int ADDR_PLAYBACK_3 = 11; // 0xb
+    field public static final int ADDR_RECORDER_1 = 1; // 0x1
+    field public static final int ADDR_RECORDER_2 = 2; // 0x2
+    field public static final int ADDR_RECORDER_3 = 9; // 0x9
+    field public static final int ADDR_RESERVED_1 = 12; // 0xc
+    field public static final int ADDR_RESERVED_2 = 13; // 0xd
+    field public static final int ADDR_TUNER_1 = 3; // 0x3
+    field public static final int ADDR_TUNER_2 = 6; // 0x6
+    field public static final int ADDR_TUNER_3 = 7; // 0x7
+    field public static final int ADDR_TUNER_4 = 10; // 0xa
+    field public static final int ADDR_TV = 0; // 0x0
+    field public static final int ADDR_UNREGISTERED = 15; // 0xf
+    field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
+    field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
+    field public static final int DEVICE_PLAYBACK = 4; // 0x4
+    field public static final int DEVICE_RECORDER = 1; // 0x1
+    field public static final int DEVICE_RESERVED = 2; // 0x2
+    field public static final int DEVICE_TUNER = 3; // 0x3
+    field public static final int DEVICE_TV = 0; // 0x0
+    field public static final int MESSAGE_ACTIVE_SOURCE = 157; // 0x9d
+  }
+
+  public final class HdmiCecMessage implements android.os.Parcelable {
+    ctor public HdmiCecMessage(int, int, int, byte[]);
+    method public int describeContents();
+    method public int getDestination();
+    method public int getOpcode();
+    method public byte[] getParams();
+    method public int getSource();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+}
+
 package android.hardware.input {
 
   public final class InputManager {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 606d803..e38bbb3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4701,6 +4701,47 @@
     }
 
     /**
+     * Set a label to be used in the Recents task display. The activities of a task are traversed
+     * in order from the topmost activity to the bottommost. As soon as one activity returns a
+     * non-null Recents label the traversal is ended and that value will be used in
+     * {@link ActivityManager.RecentTaskInfo#activityLabel}
+     *
+     * @see ActivityManager#getRecentTasks
+     *
+     * @param recentsLabel The label to use in the RecentTaskInfo.
+     */
+    public void setRecentsLabel(CharSequence recentsLabel) {
+        try {
+            ActivityManagerNative.getDefault().setRecentsLabel(mToken, recentsLabel);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Set an icon to be used in the Recents task display. The activities of a task are traversed
+     * in order from the topmost activity to the bottommost. As soon as one activity returns a
+     * non-null Recents icon the traversal is ended and that value will be used in
+     * {@link ActivityManager.RecentTaskInfo#activityIcon}.
+     *
+     * @see ActivityManager#getRecentTasks
+     *
+     * @param recentsIcon The Bitmap to use in the RecentTaskInfo.
+     */
+    public void setRecentsIcon(Bitmap recentsIcon) {
+        final Bitmap scaledIcon;
+        if (recentsIcon != null) {
+            final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
+            scaledIcon = Bitmap.createScaledBitmap(recentsIcon, size, size, true);
+        } else {
+            scaledIcon = null;
+        }
+        try {
+            ActivityManagerNative.getDefault().setRecentsIcon(mToken, scaledIcon);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Sets the visibility of the progress bar in the title.
      * <p>
      * In order for the progress bar to be shown, the feature must be requested
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0787ef1..d386eff 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -510,11 +510,23 @@
         public int stackId;
 
         /**
-         * The id the of the user the task was running as.
+         * The id of the user the task was running as.
          * @hide
          */
         public int userId;
 
+        /**
+         * The label of the highest activity in the task stack to have set a label
+         * {@link Activity#setRecentsLabel}.
+         */
+        public CharSequence activityLabel;
+
+        /**
+         * The Bitmap icon of the highest activity in the task stack to set a Bitmap using
+         * {@link Activity#setRecentsIcon}.
+         */
+        public Bitmap activityIcon;
+
         public RecentTaskInfo() {
         }
 
@@ -536,6 +548,14 @@
             ComponentName.writeToParcel(origActivity, dest);
             TextUtils.writeToParcel(description, dest,
                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            TextUtils.writeToParcel(activityLabel, dest,
+                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            if (activityIcon == null) {
+                dest.writeInt(0);
+            } else {
+                dest.writeInt(1);
+                activityIcon.writeToParcel(dest, 0);
+            }
             dest.writeInt(stackId);
             dest.writeInt(userId);
         }
@@ -550,6 +570,8 @@
             }
             origActivity = ComponentName.readFromParcel(source);
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            activityIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
             stackId = source.readInt();
             userId = source.readInt();
         }
@@ -1970,7 +1992,11 @@
      * @return dimensions of square icons in terms of pixels
      */
     public int getLauncherLargeIconSize() {
-        final Resources res = mContext.getResources();
+        return getLauncherLargeIconSizeInner(mContext);
+    }
+
+    static int getLauncherLargeIconSizeInner(Context context) {
+        final Resources res = context.getResources();
         final int size = res.getDimensionPixelSize(android.R.dimen.app_icon_size);
         final int sw = res.getConfiguration().smallestScreenWidthDp;
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 373a8a3..707a038 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2128,6 +2128,25 @@
             reply.writeInt(isInLockTaskMode ? 1 : 0);
             return true;
         }
+
+        case SET_RECENTS_LABEL_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            CharSequence recentsLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
+            setRecentsLabel(token, recentsLabel);
+            reply.writeNoException();
+            return true;
+        }
+
+        case SET_RECENTS_ICON_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            Bitmap recentsIcon = data.readInt() != 0
+                    ? Bitmap.CREATOR.createFromParcel(data) : null;
+            setRecentsIcon(token, recentsIcon);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -4899,5 +4918,36 @@
         return isInLockTaskMode;
     }
 
+    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        TextUtils.writeToParcel(recentsLabel, data, 0);
+        mRemote.transact(SET_RECENTS_LABEL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        if (recentsBitmap != null) {
+            data.writeInt(1);
+            recentsBitmap.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(SET_RECENTS_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cb06a42..3b56839 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -436,6 +436,12 @@
     /** @hide */
     public boolean isInLockTaskMode() throws RemoteException;
 
+    /** @hide */
+    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException;
+
+    /** @hide */
+    public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -735,4 +741,6 @@
     int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214;
     int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
     int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
+    int SET_RECENTS_LABEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+    int SET_RECENTS_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 12a8ff6..13e74da 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -444,41 +444,82 @@
     public static final int VISIBILITY_SECRET = -1;
 
     /**
-     * @hide
-     * Notification type: incoming call (voice or video) or similar synchronous communication request.
+     * Notification category: incoming call (voice or video) or similar synchronous communication request.
      */
-    public static final String KIND_CALL = "android.call";
+    public static final String CATEGORY_CALL = "call";
 
     /**
-     * @hide
-     * Notification type: incoming direct message (SMS, instant message, etc.).
+     * Notification category: incoming direct message (SMS, instant message, etc.).
      */
-    public static final String KIND_MESSAGE = "android.message";
+    public static final String CATEGORY_MESSAGE = "msg";
 
     /**
-     * @hide
-     * Notification type: asynchronous bulk message (email).
+     * Notification category: asynchronous bulk message (email).
      */
-    public static final String KIND_EMAIL = "android.email";
+    public static final String CATEGORY_EMAIL = "email";
 
     /**
-     * @hide
-     * Notification type: calendar event.
+     * Notification category: calendar event.
      */
-    public static final String KIND_EVENT = "android.event";
+    public static final String CATEGORY_EVENT = "event";
 
     /**
-     * @hide
-     * Notification type: promotion or advertisement.
+     * Notification category: promotion or advertisement.
      */
-    public static final String KIND_PROMO = "android.promo";
+    public static final String CATEGORY_PROMO = "promo";
 
     /**
-     * @hide
-     * If this notification matches of one or more special types (see the <code>KIND_*</code>
-     * constants), add them here, best match first.
+     * Notification category: alarm or timer.
      */
-    public String[] kind;
+    public static final String CATEGORY_ALARM = "alarm";
+
+    /**
+     * Notification category: progress of a long-running background operation.
+     */
+    public static final String CATEGORY_PROGRESS = "progress";
+
+    /**
+     * Notification category: social network or sharing update.
+     */
+    public static final String CATEGORY_SOCIAL = "social";
+
+    /**
+     * Notification category: error in background operation or authentication status.
+     */
+    public static final String CATEGORY_ERROR = "err";
+
+    /**
+     * Notification category: media transport control for playback.
+     */
+    public static final String CATEGORY_TRANSPORT = "transport";
+
+    /**
+     * Notification category: system or device status update.  Reserved for system use.
+     */
+    public static final String CATEGORY_SYSTEM = "sys";
+
+    /**
+     * Notification category: indication of running background service.
+     */
+    public static final String CATEGORY_SERVICE = "service";
+
+    /**
+     * Notification category: a specific, timely recommendation for a single thing.
+     * For example, a news app might want to recommend a news story it believes the user will
+     * want to read next.
+     */
+    public static final String CATEGORY_RECOMMENDATION = "recommendation";
+
+    /**
+     * Notification category: ongoing information about device or contextual status.
+     */
+    public static final String CATEGORY_STATUS = "status";
+
+    /**
+     * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
+     * that best describes this Notification.  May be used by the system for ranking and filtering.
+     */
+    public String category;
 
     /**
      * Additional semantic data to be carried around with this Notification.
@@ -619,6 +660,13 @@
     public static final String EXTRA_BUILDER_REMOTE_VIEWS = "android.builderRemoteViews";
 
     /**
+     * Allow certain system-generated notifications to appear before the device is provisioned.
+     * Only available to notifications coming from the android package.
+     * @hide
+     */
+    public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
+
+    /**
      * Value for {@link #EXTRA_AS_HEADS_UP}.
      * @hide
      */
@@ -815,7 +863,7 @@
 
         priority = parcel.readInt();
 
-        kind = parcel.createStringArray(); // may set kind to null
+        category = parcel.readString();
 
         extras = parcel.readBundle(); // may be null
 
@@ -890,12 +938,7 @@
 
         that.priority = this.priority;
 
-        final String[] thiskind = this.kind;
-        if (thiskind != null) {
-            final int N = thiskind.length;
-            final String[] thatkind = that.kind = new String[N];
-            System.arraycopy(thiskind, 0, thatkind, 0, N);
-        }
+        that.category = this.category;
 
         if (this.extras != null) {
             try {
@@ -1044,7 +1087,7 @@
 
         parcel.writeInt(priority);
 
-        parcel.writeStringArray(kind); // ok for null
+        parcel.writeString(category);
 
         parcel.writeBundle(extras); // null ok
 
@@ -1180,16 +1223,7 @@
         sb.append(Integer.toHexString(this.defaults));
         sb.append(" flags=0x");
         sb.append(Integer.toHexString(this.flags));
-        sb.append(" kind=[");
-        if (this.kind == null) {
-            sb.append("null");
-        } else {
-            for (int i=0; i<this.kind.length; i++) {
-                if (i>0) sb.append(",");
-                sb.append(this.kind[i]);
-            }
-        }
-        sb.append("]");
+        sb.append(" category="); sb.append(this.category);
         if (actions != null) {
             sb.append(" ");
             sb.append(actions.length);
@@ -1271,7 +1305,7 @@
         private int mProgressMax;
         private int mProgress;
         private boolean mProgressIndeterminate;
-        private ArrayList<String> mKindList = new ArrayList<String>(1);
+        private String mCategory;
         private Bundle mExtras;
         private int mPriority;
         private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
@@ -1679,14 +1713,12 @@
         }
 
         /**
-         * @hide
+         * Set the notification category.
          *
-         * Add a kind (category) to this notification. Optional.
-         *
-         * @see Notification#kind
+         * @see Notification#category
          */
-        public Builder addKind(String k) {
-            mKindList.add(k);
+        public Builder setCategory(String category) {
+            mCategory = category;
             return this;
         }
 
@@ -2010,12 +2042,7 @@
             if ((mDefaults & DEFAULT_LIGHTS) != 0) {
                 n.flags |= FLAG_SHOW_LIGHTS;
             }
-            if (mKindList.size() > 0) {
-                n.kind = new String[mKindList.size()];
-                mKindList.toArray(n.kind);
-            } else {
-                n.kind = null;
-            }
+            n.category = mCategory;
             n.priority = mPriority;
             if (mActions.size() > 0) {
                 n.actions = new Action[mActions.size()];
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
new file mode 100644
index 0000000..c95431a
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -0,0 +1,190 @@
+/*
+ * 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.hardware.hdmi;
+
+/**
+ * Defines constants and utility methods related to HDMI-CEC protocol.
+ */
+public final class HdmiCec {
+
+    /** TV device type. */
+    public static final int DEVICE_TV = 0;
+
+    /** Recording device type. */
+    public static final int DEVICE_RECORDER = 1;
+
+    /** Device type reserved for future usage. */
+    public static final int DEVICE_RESERVED = 2;
+
+    /** Tuner device type. */
+    public static final int DEVICE_TUNER = 3;
+
+    /** Playback device type. */
+    public static final int DEVICE_PLAYBACK = 4;
+
+    /** Audio system device type. */
+    public static final int DEVICE_AUDIO_SYSTEM = 5;
+
+    // Value indicating the device is not an active source.
+    public static final int DEVICE_INACTIVE = -1;
+
+    /** Logical address for TV */
+    public static final int ADDR_TV = 0;
+
+    /** Logical address for recorder 1 */
+    public static final int ADDR_RECORDER_1 = 1;
+
+    /** Logical address for recorder 2 */
+    public static final int ADDR_RECORDER_2 = 2;
+
+    /** Logical address for tuner 1 */
+    public static final int ADDR_TUNER_1 = 3;
+
+    /** Logical address for playback 1 */
+    public static final int ADDR_PLAYBACK_1 = 4;
+
+    /** Logical address for audio system */
+    public static final int ADDR_AUDIO_SYSTEM = 5;
+
+    /** Logical address for tuner 2 */
+    public static final int ADDR_TUNER_2 = 6;
+
+    /** Logical address for tuner 3 */
+    public static final int ADDR_TUNER_3 = 7;
+
+    /** Logical address for playback 2 */
+    public static final int ADDR_PLAYBACK_2 = 8;
+
+    /** Logical address for recorder 3 */
+    public static final int ADDR_RECORDER_3 = 9;
+
+    /** Logical address for tuner 4 */
+    public static final int ADDR_TUNER_4 = 10;
+
+    /** Logical address for playback 3 */
+    public static final int ADDR_PLAYBACK_3 = 11;
+
+    /** Logical address reserved for future usage */
+    public static final int ADDR_RESERVED_1 = 12;
+
+    /** Logical address reserved for future usage */
+    public static final int ADDR_RESERVED_2 = 13;
+
+    /** Logical address for TV other than the one assigned with {@link #ADDR_TV} */
+    public static final int ADDR_FREE_USE = 14;
+
+    /** Logical address for devices to which address cannot be allocated */
+    public static final int ADDR_UNREGISTERED = 15;
+
+    /** Logical address used in the destination address field for broadcast messages */
+    public static final int ADDR_BROADCAST = 15;
+
+    /** Logical address used to indicate it is not initialized or invalid. */
+    public static final int ADDR_INVALID = -1;
+
+    // TODO: Complete the list of CEC messages definition.
+    public static final int MESSAGE_ACTIVE_SOURCE = 0x9D;
+
+    private static final int[] ADDRESS_TO_TYPE = {
+        DEVICE_TV,  // ADDR_TV
+        DEVICE_RECORDER,  // ADDR_RECORDER_1
+        DEVICE_RECORDER,  // ADDR_RECORDER_2
+        DEVICE_TUNER,  // ADDR_TUNER_1
+        DEVICE_PLAYBACK,  // ADDR_PLAYBACK_1
+        DEVICE_AUDIO_SYSTEM,  // ADDR_AUDIO_SYSTEM
+        DEVICE_TUNER,  // ADDR_TUNER_2
+        DEVICE_TUNER,  // ADDR_TUNER_3
+        DEVICE_PLAYBACK,  // ADDR_PLAYBACK_2
+        DEVICE_RECORDER,  // ADDR_RECORDER_3
+        DEVICE_TUNER,  // ADDR_TUNER_4
+        DEVICE_PLAYBACK,  // ADDR_PLAYBACK_3
+    };
+
+    private static final String[] DEFAULT_NAMES = {
+        "TV",
+        "Recorder_1",
+        "Recorder_2",
+        "Tuner_1",
+        "Playback_1",
+        "AudioSystem",
+        "Tuner_2",
+        "Tuner_3",
+        "Playback_2",
+        "Recorder_3",
+        "Tuner_4",
+        "Playback_3",
+    };
+
+    private HdmiCec() { }  // Prevents instantiation.
+
+    /**
+     * Check if the given type is valid. A valid type is one of the actual
+     * logical device types defined in the standard ({@link #DEVICE_TV},
+     * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
+     * and {@link #DEVICE_AUDIO_SYSTEM}).
+     *
+     * @param type device type
+     * @return true if the given type is valid
+     */
+    public static boolean isValidType(int type) {
+        return (DEVICE_TV <= type && type <= DEVICE_AUDIO_SYSTEM)
+                && type != DEVICE_RESERVED;
+    }
+
+    /**
+     * Check if the given logical address is valid. A logical address is valid
+     * if it is one allocated for an actual device which allows communication
+     * with other logical devices.
+     *
+     * @param address logical address
+     * @return true if the given address is valid
+     */
+    public static boolean isValidAddress(int address) {
+        // TODO: We leave out the address 'free use(14)' for now. Check this later
+        //       again to make sure it is a valid address for communication.
+        return (ADDR_TV <= address && address <= ADDR_PLAYBACK_3);
+    }
+
+    /**
+     * Return the device type for the given logical address.
+     *
+     * @param address logical address
+     * @return device type for the given logical address; DEVICE_INACTIVE
+     *         if the address is not valid.
+     */
+    public static int getTypeFromAddress(int address) {
+        if (isValidAddress(address)) {
+            return ADDRESS_TO_TYPE[address];
+        }
+        return DEVICE_INACTIVE;
+    }
+
+    /**
+     * Return the default device name for a logical address. This is the name
+     * by which the logical device is known to others until a name is
+     * set explicitly using HdmiCecService.setOsdName.
+     *
+     * @param address logical address
+     * @return default device name; empty string if the address is not valid
+     */
+    public static String getDefaultDeviceName(int address) {
+        if (isValidAddress(address)) {
+            return DEFAULT_NAMES[address];
+        }
+        return "";
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.aidl b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
new file mode 100644
index 0000000..6687ba4
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.hardware.hdmi;
+
+parcelable HdmiCecMessage;
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
new file mode 100644
index 0000000..be94d97
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -0,0 +1,145 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * A class to encapsulate HDMI-CEC message used for the devices connected via
+ * HDMI cable to communicate with one another. A message is defined by its
+ * source and destination address, command (or opcode), and optional parameters.
+ */
+public final class HdmiCecMessage implements Parcelable {
+
+    private static final int MAX_MESSAGE_LENGTH = 16;
+
+    private final int mSource;
+    private final int mDestination;
+
+    private final int mOpcode;
+    private final byte[] mParams;
+
+    /**
+     * Constructor.
+     */
+    public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+        mSource = source;
+        mDestination = destination;
+        mOpcode = opcode;
+        mParams = Arrays.copyOf(params, params.length);
+    }
+
+    /**
+     * Return the source address field of the message. It is the logical address
+     * of the device which generated the message.
+     *
+     * @return source address
+     */
+    public int getSource() {
+        return mSource;
+    }
+
+    /**
+     * Return the destination address field of the message. It is the logical address
+     * of the device to which the message is sent.
+     *
+     * @return destination address
+     */
+    public int getDestination() {
+        return mDestination;
+    }
+
+    /**
+     * Return the opcode field of the message. It is the type of the message that
+     * tells the destination device what to do.
+     *
+     * @return opcode
+     */
+    public int getOpcode() {
+        return mOpcode;
+    }
+
+    /**
+     * Return the parameter field of the message. The contents of parameter varies
+     * from opcode to opcode, and is used together with opcode to describe
+     * the action for the destination device to take.
+     *
+     * @return parameter
+     */
+    public byte[] getParams() {
+        return mParams;
+    }
+
+    /**
+     * Describe the kinds of special objects contained in this Parcelable's
+     * marshalled representation.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *        May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSource);
+        dest.writeInt(mDestination);
+        dest.writeInt(mOpcode);
+        dest.writeInt(mParams.length);
+        dest.writeByteArray(mParams);
+    }
+
+    public static final Parcelable.Creator<HdmiCecMessage> CREATOR
+            = new Parcelable.Creator<HdmiCecMessage>() {
+        /**
+         * Rebuild a HdmiCecMessage previously stored with writeToParcel().
+         * @param p HdmiCecMessage object to read the Rating from
+         * @return a new HdmiCecMessage created from the data in the parcel
+         */
+        public HdmiCecMessage createFromParcel(Parcel p) {
+            int source = p.readInt();
+            int destination = p.readInt();
+            int opcode = p.readInt();
+            byte[] params = new byte[p.readInt()];
+            p.readByteArray(params);
+            return new HdmiCecMessage(source, destination, opcode, params);
+        }
+        public HdmiCecMessage[] newArray(int size) {
+            return new HdmiCecMessage[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        StringBuffer s = new StringBuffer();
+        s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode));
+        for (byte data : mParams) {
+            s.append(String.format("%02X ", data));
+        }
+        return s.toString();
+    }
+}
+
diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
new file mode 100644
index 0000000..d281ce6
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+
+/**
+ * Interface definition for HdmiCecService to do interprocess communcation.
+ *
+ * @hide
+ */
+oneway interface IHdmiCecListener {
+    void onMessageReceived(in HdmiCecMessage message);
+    void onCableStatusChanged(in boolean connected);
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
new file mode 100644
index 0000000..6fefcf8
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.os.IBinder;
+
+/**
+ * Binder interface that components running in the appplication process
+ * will use to enable HDMI-CEC protocol exchange with other devices.
+ *
+ * @hide
+ */
+interface IHdmiCecService {
+    IBinder allocateLogicalDevice(int type, IHdmiCecListener listener);
+    void removeServiceListener(IBinder b, IHdmiCecListener listener);
+    void setOsdName(IBinder b, String name);
+    void sendActiveSource(IBinder b);
+    void sendInactiveSource(IBinder b);
+    void sendImageViewOn(IBinder b);
+    void sendTextViewOn(IBinder b);
+    void sendGiveDevicePowerStatus(IBinder b, int address);
+    void sendMessage(IBinder b, in HdmiCecMessage message);
+}
+
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b0d94d5..87beb95 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -155,6 +155,7 @@
     private static final String FOREGROUND_DATA = "fg";
     private static final String WAKELOCK_DATA = "wl";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
+    private static final String WAKEUP_REASON_DATA = "wr";
     private static final String NETWORK_DATA = "nt";
     private static final String USER_ACTIVITY_DATA = "ua";
     private static final String BATTERY_DATA = "bt";
@@ -201,6 +202,25 @@
     }
 
     /**
+     * State for keeping track of long counting information.
+     */
+    public static abstract class LongCounter {
+
+        /**
+         * Returns the count associated with this Counter for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         */
+        public abstract long getCountLocked(int which);
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
      * State for keeping track of timing information.
      */
     public static abstract class Timer {
@@ -552,8 +572,8 @@
         // These states always appear directly in the first int token
         // of a delta change; they should be ones that change relatively
         // frequently.
-        public static final int STATE_WAKE_LOCK_FLAG = 1<<31;
-        public static final int STATE_SENSOR_ON_FLAG = 1<<30;
+        public static final int STATE_CPU_RUNNING_FLAG = 1<<31;
+        public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
         public static final int STATE_GPS_ON_FLAG = 1<<29;
         public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
         public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
@@ -562,9 +582,9 @@
         public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
         // These are on the lower bits used for the command; if they change
         // we need to write another int of data.
-        public static final int STATE_PHONE_SCANNING_FLAG = 1<<23;
+        public static final int STATE_SENSOR_ON_FLAG = 1<<23;
         public static final int STATE_AUDIO_ON_FLAG = 1<<22;
-        public static final int STATE_VIDEO_ON_FLAG = 1<<21;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
         public static final int STATE_SCREEN_ON_FLAG = 1<<20;
         public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
         public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
@@ -577,6 +597,7 @@
 
         public int states;
 
+        public static final int STATE2_VIDEO_ON_FLAG = 1<<0;
         public int states2;
 
         // The wake lock that was acquired at this point.
@@ -653,6 +674,7 @@
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(states);
+            dest.writeInt(states2);
             if (wakelockTag != null) {
                 wakelockTag.writeToParcel(dest, flags);
             }
@@ -680,6 +702,7 @@
             batteryTemperature = (short)(bat2&0xffff);
             batteryVoltage = (char)((bat2>>16)&0xffff);
             states = src.readInt();
+            states2 = src.readInt();
             if ((bat&0x10000000) != 0) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.readFromParcel(src);
@@ -718,6 +741,7 @@
             batteryTemperature = 0;
             batteryVoltage = 0;
             states = 0;
+            states2 = 0;
             wakelockTag = null;
             wakeReasonTag = null;
             eventCode = EVENT_NONE;
@@ -744,6 +768,7 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             states = o.states;
+            states2 = o.states2;
             if (o.wakelockTag != null) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.setTo(o.wakelockTag);
@@ -774,6 +799,7 @@
                     && batteryTemperature == o.batteryTemperature
                     && batteryVoltage == o.batteryVoltage
                     && states == o.states
+                    && states2 == o.states2
                     && currentTime == o.currentTime;
         }
 
@@ -970,6 +996,15 @@
     public abstract int getMobileRadioActiveCount(int which);
 
     /**
+     * Returns the time in microseconds that is the difference between the mobile radio
+     * time we saw based on the elapsed timestamp when going down vs. the given time stamp
+     * from the radio.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioActiveAdjustedTime(int which);
+
+    /**
      * Returns the time in microseconds that the mobile network has been active
      * (in a high power state) but not being able to blame on an app.
      *
@@ -1026,9 +1061,10 @@
      * {@hide}
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
-    
+
     public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
             = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
         new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
         new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
         new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps", "g"),
@@ -1039,7 +1075,6 @@
         new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
         new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
-        new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
         new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
@@ -1062,6 +1097,11 @@
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
     };
 
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
+            = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
+    };
+
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync"
     };
@@ -1294,6 +1334,8 @@
      */
     public abstract long computeRealtime(long curTime, int which);
     
+    public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
+
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
     /** Returns the number of different speeds that the CPU can run at */
@@ -1554,7 +1596,8 @@
                 wifiRunningTime / 1000, bluetoothOnTime / 1000,
                 mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
                 fullWakeLockTimeTotal, partialWakeLockTimeTotal,
-                getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which));
+                getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which),
+                getMobileRadioActiveAdjustedTime(which));
         
         // Dump screen brightness stats
         Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -1626,16 +1669,22 @@
         }
         
         if (reqUid < 0) {
-            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
                     sb.setLength(0);
                     printWakeLockCheckin(sb, ent.getValue(), rawRealtime, null, which, "");
-    
-                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
+                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(),
                             sb.toString());
                 }
             }
+            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                    dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
+                            "\"" + ent.getKey() + "\"", ent.getValue().getCountLocked(which));
+                }
+            }
         }
         
         BatteryStatsHelper helper = new BatteryStatsHelper(context);
@@ -2111,6 +2160,18 @@
             pw.println(sb.toString());
         }
 
+        final long mobileActiveAdjustedTime = getMobileRadioActiveAdjustedTime(which);
+        if (mobileActiveAdjustedTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Mobile radio active adjusted time: ");
+            formatTimeMs(sb, mobileActiveAdjustedTime / 1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(mobileActiveAdjustedTime, whichBatteryRealtime));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
         pw.print(prefix);
                 pw.print("  Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotalBytes));
                 pw.print(", sent: "); pw.print(formatBytesLocked(wifiTxTotalBytes));
@@ -2346,24 +2407,49 @@
                     pw.println();
                 }
             }
-        }
 
-        if (timers.size() > 0) {
-            Collections.sort(timers, timerComparator);
-            pw.print(prefix); pw.println("  All partial wake locks:");
-            for (int i=0; i<timers.size(); i++) {
-                TimerEntry timer = timers.get(i);
-                sb.setLength(0);
-                sb.append("  Wake lock ");
-                UserHandle.formatUid(sb, timer.mId);
-                sb.append(" ");
-                sb.append(timer.mName);
-                printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
-                sb.append(" realtime");
-                pw.println(sb.toString());
+            if (timers.size() > 0) {
+                Collections.sort(timers, timerComparator);
+                pw.print(prefix); pw.println("  All partial wake locks:");
+                for (int i=0; i<timers.size(); i++) {
+                    TimerEntry timer = timers.get(i);
+                    sb.setLength(0);
+                    sb.append("  Wake lock ");
+                    UserHandle.formatUid(sb, timer.mId);
+                    sb.append(" ");
+                    sb.append(timer.mName);
+                    printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+                    sb.append(" realtime");
+                    pw.println(sb.toString());
+                }
+                timers.clear();
+                pw.println();
             }
-            timers.clear();
-            pw.println();
+
+            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                pw.print(prefix); pw.println("  All wakeup reasons:");
+                final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
+                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                    BatteryStats.LongCounter counter = ent.getValue();
+                    reasons.add(new TimerEntry(ent.getKey(), 0, null,
+                            ent.getValue().getCountLocked(which)));
+                }
+                Collections.sort(reasons, timerComparator);
+                for (int i=0; i<reasons.size(); i++) {
+                    TimerEntry timer = reasons.get(i);
+                    String linePrefix = ": ";
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("  Wakeup reason ");
+                    sb.append(timer.mName);
+                    sb.append(": ");
+                    formatTimeMs(sb, timer.mTime);
+                    sb.append("realtime");
+                    pw.println(sb.toString());
+                }
+                pw.println();
+            }
         }
 
         for (int iu=0; iu<NU; iu++) {
@@ -2772,6 +2858,7 @@
 
     public static class HistoryPrinter {
         int oldState = 0;
+        int oldState2 = 0;
         int oldLevel = -1;
         int oldStatus = -1;
         int oldHealth = -1;
@@ -2780,7 +2867,8 @@
         int oldVolt = -1;
         long lastTime = -1;
 
-        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+                boolean verbose) {
             if (!checkin) {
                 pw.print("  ");
                 if (now >= 0) {
@@ -2831,16 +2919,18 @@
                     if (rec.batteryLevel < 10) pw.print("00");
                     else if (rec.batteryLevel < 100) pw.print("0");
                     pw.print(rec.batteryLevel);
-                    pw.print(" ");
-                    if (rec.states < 0) ;
-                    else if (rec.states < 0x10) pw.print("0000000");
-                    else if (rec.states < 0x100) pw.print("000000");
-                    else if (rec.states < 0x1000) pw.print("00000");
-                    else if (rec.states < 0x10000) pw.print("0000");
-                    else if (rec.states < 0x100000) pw.print("000");
-                    else if (rec.states < 0x1000000) pw.print("00");
-                    else if (rec.states < 0x10000000) pw.print("0");
-                    pw.print(Integer.toHexString(rec.states));
+                    if (verbose) {
+                        pw.print(" ");
+                        if (rec.states < 0) ;
+                        else if (rec.states < 0x10) pw.print("0000000");
+                        else if (rec.states < 0x100) pw.print("000000");
+                        else if (rec.states < 0x1000) pw.print("00000");
+                        else if (rec.states < 0x10000) pw.print("0000");
+                        else if (rec.states < 0x100000) pw.print("000");
+                        else if (rec.states < 0x1000000) pw.print("00");
+                        else if (rec.states < 0x10000000) pw.print("0");
+                        pw.print(Integer.toHexString(rec.states));
+                    }
                 } else {
                     if (oldLevel != rec.batteryLevel) {
                         oldLevel = rec.batteryLevel;
@@ -2934,6 +3024,8 @@
                 }
                 printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
+                printBitDescriptions(pw, oldState2, rec.states2, null,
+                        HISTORY_STATE2_DESCRIPTIONS, !checkin);
                 if (rec.wakeReasonTag != null) {
                     if (checkin) {
                         pw.print(",Wr=");
@@ -3010,6 +3102,7 @@
     public static final int DUMP_CHARGED_ONLY = 1<<1;
     public static final int DUMP_HISTORY_ONLY = 1<<2;
     public static final int DUMP_INCLUDE_HISTORY = 1<<3;
+    public static final int DUMP_VERBOSE = 1<<4;
 
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
@@ -3047,7 +3140,8 @@
                     while (getNextHistoryLocked(rec)) {
                         lastTime = rec.time;
                         if (rec.time >= histStart) {
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+                                    (flags&DUMP_VERBOSE) != 0);
                         }
                     }
                     if (histStart >= 0) {
@@ -3064,7 +3158,7 @@
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     while (getNextOldHistoryLocked(rec)) {
-                        hprinter.printNextItem(pw, rec, now, false);
+                        hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
                     }
                     pw.println();
                 } finally {
@@ -3150,7 +3244,7 @@
                         if (rec.time >= histStart) {
                             pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                             pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true);
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
                         }
                     }
                     if (histStart >= 0) {
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index b97734e..4180860 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -694,6 +694,36 @@
                 return _result;
             }
 
+            public String getPassword() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_getPassword, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            public void clearPassword() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_clearPassword, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
             public StorageVolume[] getVolumeList() throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -846,7 +876,11 @@
 
         static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
 
-        static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 36;
+        static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 35;
+
+        static final int TRANSACTION_getPassword = IBinder.FIRST_CALL_TRANSACTION + 36;
+
+        static final int TRANSACTION_clearPassword = IBinder.FIRST_CALL_TRANSACTION + 37;
 
         /**
          * Cast an IBinder object into an IMountService interface, generating a
@@ -1208,6 +1242,19 @@
                     reply.writeInt(result);
                     return true;
                 }
+                case TRANSACTION_getPassword: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String result = getPassword();
+                    reply.writeNoException();
+                    reply.writeString(result);
+                    return true;
+                }
+                case TRANSACTION_clearPassword: {
+                    data.enforceInterface(DESCRIPTOR);
+                    clearPassword();
+                    reply.writeNoException();
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1446,4 +1493,15 @@
      * @return PasswordType
      */
     public int getPasswordType() throws RemoteException;
+
+    /**
+     * Get password from vold
+     * @return password or empty string
+     */
+    public String getPassword() throws RemoteException;
+
+    /**
+     * Securely clear password from vold
+     */
+    public void clearPassword() throws RemoteException;
 }
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 6381884..719dcea 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -21,15 +21,16 @@
 import java.util.Locale;
 
 /**
- * The Indexable data for Search. This abstract class defines the common parts for all search
- * indexable data.
+ * The Indexable data for Search.
+ *
+ * This abstract class defines the common parts for all search indexable data.
  *
  * @hide
  */
 public abstract class SearchIndexableData {
 
     /**
-     * The context for the data. Will usually allow to retrieve some resources.
+     * The context for the data. Will usually allow retrieving some resources.
      *
      * @see Context
      */
@@ -41,6 +42,11 @@
     public Locale locale;
 
     /**
+     * Tells if the data will be included into the search results. This is application specific.
+     */
+    public boolean enabled;
+
+    /**
      * The rank for the data. This is application specific.
      */
     public int rank;
@@ -103,6 +109,7 @@
      * Default constructor.
      */
     public SearchIndexableData() {
+        enabled = true;
     }
 
     /**
@@ -113,5 +120,6 @@
     public SearchIndexableData(Context ctx) {
         context = ctx;
         locale = Locale.getDefault();
+        enabled = true;
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a8ccd49..c5835da 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4210,7 +4210,7 @@
             out.append(" #");
             out.append(Integer.toHexString(id));
             final Resources r = mResources;
-            if (id != 0 && r != null) {
+            if (Resources.resourceHasPackage(id) && r != null) {
                 try {
                     String pkgname;
                     switch (id&0xff000000) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1d6e998..a94a671 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -659,14 +659,14 @@
         mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
     }
 
-    public void attachFunctor(int functor) {
+    public void attachFunctor(long functor) {
         //noinspection SimplifiableIfStatement
         if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
             mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
         }
     }
 
-    public void detachFunctor(int functor) {
+    public void detachFunctor(long functor) {
         mBlockResizeBuffer = true;
         if (mAttachInfo.mHardwareRenderer != null) {
             mAttachInfo.mHardwareRenderer.detachFunctor(functor);
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 52f9c0b..b05225b 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -787,7 +787,7 @@
             return;
         }
 
-        mVibrator.vibrate(VIBRATE_DURATION);
+        mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
     }
 
     protected void onRemoteVolumeChanged(int streamType, int flags) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5ba5c57..32f700d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -55,7 +55,7 @@
     void noteScreenOff();
     void noteInputEvent();
     void noteUserActivity(int uid, int event);
-    void noteDataConnectionActive(int type, boolean active);
+    void noteDataConnectionActive(int type, boolean active, long timestampNs);
     void notePhoneOn();
     void notePhoneOff();
     void notePhoneSignalStrength(in SignalStrength signalStrength);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 082f1a5..1d88533 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,7 +87,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 99 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 101 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -191,7 +191,7 @@
 
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
-    boolean mRecordingHistory = true;
+    boolean mRecordingHistory = false;
     int mNumHistoryItems;
 
     static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
@@ -200,6 +200,7 @@
     final HistoryItem mHistoryLastWritten = new HistoryItem();
     final HistoryItem mHistoryLastLastWritten = new HistoryItem();
     final HistoryItem mHistoryReadTmp = new HistoryItem();
+    final HistoryItem mHistoryAddTmp = new HistoryItem();
     final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
     String[] mReadHistoryStrings;
     int[] mReadHistoryUids;
@@ -209,7 +210,8 @@
     int mHistoryBufferLastPos = -1;
     boolean mHistoryOverflow = false;
     long mLastHistoryElapsedRealtime = 0;
-    long mLastHistoryUptime = 0;
+    long mTrackRunningHistoryElapsedRealtime = 0;
+    long mTrackRunningHistoryUptime = 0;
 
     final HistoryItem mHistoryCur = new HistoryItem();
 
@@ -287,6 +289,7 @@
     boolean mMobileRadioActive;
     StopwatchTimer mMobileRadioActiveTimer;
     StopwatchTimer mMobileRadioActivePerAppTimer;
+    LongSamplingCounter mMobileRadioActiveAdjustedTime;
     LongSamplingCounter mMobileRadioActiveUnknownTime;
     LongSamplingCounter mMobileRadioActiveUnknownCount;
 
@@ -330,12 +333,21 @@
     private final HashMap<String, SamplingTimer> mKernelWakelockStats =
             new HashMap<String, SamplingTimer>();
 
-    public Map<String, ? extends SamplingTimer> getKernelWakelockStats() {
+    public Map<String, ? extends Timer> getKernelWakelockStats() {
         return mKernelWakelockStats;
     }
 
     private static int sKernelWakelockUpdateVersion = 0;
 
+    String mLastWakeupReason = null;
+    long mLastWakeupUptimeMs = 0;
+    private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
+            new HashMap<String, LongSamplingCounter>();
+
+    public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+        return mWakeupReasonStats;
+    }
+
     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
                               Process.PROC_QUOTES,
@@ -719,7 +731,7 @@
         }
     }
 
-    public static class LongSamplingCounter implements TimeBaseObs {
+    public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
         final TimeBase mTimeBase;
         long mCount;
         long mLoadedCount;
@@ -775,6 +787,14 @@
             return val;
         }
 
+        @Override
+        public void logState(Printer pw, String prefix) {
+            pw.println(prefix + "mCount=" + mCount
+                    + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+                    + " mUnpluggedCount=" + mUnpluggedCount
+                    + " mPluggedCount=" + mPluggedCount);
+        }
+
         void addCountLocked(long count) {
             mCount += count;
         }
@@ -1409,6 +1429,10 @@
             return 0;
         }
 
+        long getLastUpdateTimeMs() {
+            return mUpdateTime;
+        }
+
         void stopRunningLocked(long elapsedRealtimeMs) {
             // Ignore attempt to stop a timer that isn't running
             if (mNesting == 0) {
@@ -1502,6 +1526,19 @@
         }
     }
 
+    /*
+     * Get the wakeup reason counter, and create a new one if one
+     * doesn't already exist.
+     */
+    public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
+        LongSamplingCounter counter = mWakeupReasonStats.get(name);
+        if (counter == null) {
+            counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+            mWakeupReasonStats.put(name, counter);
+        }
+        return counter;
+    }
+
     private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
 
         FileInputStream is;
@@ -1759,6 +1796,10 @@
         if (stateIntChanged) {
             firstToken |= DELTA_STATE_FLAG;
         }
+        final boolean state2IntChanged = cur.states2 != last.states2;
+        if (state2IntChanged) {
+            firstToken |= DELTA_STATE2_FLAG;
+        }
         if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             firstToken |= DELTA_WAKELOCK_FLAG;
         }
@@ -1795,6 +1836,11 @@
                     + " batteryPlugType=" + cur.batteryPlugType
                     + " states=0x" + Integer.toHexString(cur.states));
         }
+        if (state2IntChanged) {
+            dest.writeInt(cur.states2);
+            if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+                    + Integer.toHexString(cur.states2));
+        }
         if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             int wakeLockIndex;
             int wakeReasonIndex;
@@ -1917,6 +1963,12 @@
             cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~DELTA_STATE_MASK));
         }
 
+        if ((firstToken&DELTA_STATE2_FLAG) != 0) {
+            cur.states2 = src.readInt();
+            if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x"
+                    + Integer.toHexString(cur.states2));
+        }
+
         if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
             int indexes = src.readInt();
             int wakeLockIndex = indexes&0xffff;
@@ -1958,29 +2010,34 @@
         }
     }
 
-    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs) {
+    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
 
         final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
-        final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
+        final int diffStates = mHistoryLastWritten.states^cur.states;
+        final int diffStates2 = mHistoryLastWritten.states2^cur.states2;
         final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
+        final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
         if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
                 + Integer.toHexString(diffStates) + " lastDiff="
-                + Integer.toHexString(lastDiffStates));
+                + Integer.toHexString(lastDiffStates) + " diff2="
+                + Integer.toHexString(diffStates2) + " lastDiff2="
+                + Integer.toHexString(lastDiffStates2));
         if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                 && timeDiff < 1000 && (diffStates&lastDiffStates) == 0
-                && (mHistoryLastWritten.wakelockTag == null || mHistoryCur.wakelockTag == null)
-                && (mHistoryLastWritten.wakeReasonTag == null || mHistoryCur.wakeReasonTag == null)
+                && (diffStates2&lastDiffStates2) == 0
+                && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+                && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
                 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
-                        || mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
-                && mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
-                && mHistoryLastWritten.batteryStatus == mHistoryCur.batteryStatus
-                && mHistoryLastWritten.batteryHealth == mHistoryCur.batteryHealth
-                && mHistoryLastWritten.batteryPlugType == mHistoryCur.batteryPlugType
-                && mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
-                && mHistoryLastWritten.batteryVoltage == mHistoryCur.batteryVoltage) {
+                        || cur.eventCode == HistoryItem.EVENT_NONE)
+                && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+                && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+                && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+                && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+                && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+                && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
             // We can merge this new change in with the last one.  Merging is
             // allowed as long as only the states have changed, and within those states
             // as long as no bit has changed both between now and the last entry, as
@@ -1994,23 +2051,23 @@
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
             if (mHistoryLastWritten.wakelockTag != null) {
-                mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                mHistoryCur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+                cur.wakelockTag = cur.localWakelockTag;
+                cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
             }
             // If the last written history had a wake reason tag, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
             if (mHistoryLastWritten.wakeReasonTag != null) {
-                mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
-                mHistoryCur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+                cur.wakeReasonTag = cur.localWakeReasonTag;
+                cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
             }
             // If the last written history had an event, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have an event.
             if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
-                mHistoryCur.eventCode = mHistoryLastWritten.eventCode;
-                mHistoryCur.eventTag = mHistoryCur.localEventTag;
-                mHistoryCur.eventTag.setTo(mHistoryLastWritten.eventTag);
+                cur.eventCode = mHistoryLastWritten.eventCode;
+                cur.eventTag = cur.localEventTag;
+                cur.eventTag.setTo(mHistoryLastWritten.eventTag);
             }
             mHistoryLastWritten.setTo(mHistoryLastLastWritten);
         }
@@ -2019,8 +2076,8 @@
         if (dataSize >= MAX_HISTORY_BUFFER) {
             if (!mHistoryOverflow) {
                 mHistoryOverflow = true;
-                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
-                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
                 return;
             }
 
@@ -2028,43 +2085,64 @@
             // record changes to the battery level and the most interesting states.
             // Once we've reached the maximum maximum number of items, we only
             // record changes to the battery level.
-            if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+            if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
                     (dataSize >= MAX_MAX_HISTORY_BUFFER
-                            || ((mHistoryLastWritten.states^mHistoryCur.states)
+                            || ((mHistoryLastWritten.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
 
-            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
             return;
         }
 
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
     }
 
-    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
+    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
+            HistoryItem cur) {
         if (mIteratingHistory) {
             throw new IllegalStateException("Can't do this while iterating history!");
         }
         mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
         mHistoryLastLastWritten.setTo(mHistoryLastWritten);
-        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
+        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
         mLastHistoryElapsedRealtime = elapsedRealtimeMs;
-        mLastHistoryUptime = uptimeMs;
-        mHistoryCur.wakelockTag = null;
-        mHistoryCur.wakeReasonTag = null;
-        mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
-        mHistoryCur.eventTag = null;
+        cur.wakelockTag = null;
+        cur.wakeReasonTag = null;
+        cur.eventCode = HistoryItem.EVENT_NONE;
+        cur.eventTag = null;
         if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
                 + " now " + mHistoryBuffer.dataPosition()
                 + " size is now " + mHistoryBuffer.dataSize());
     }
 
     int mChangedStates = 0;
+    int mChangedStates2 = 0;
 
     void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
+        if (mTrackRunningHistoryElapsedRealtime != 0) {
+            final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
+            final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
+            if (diffUptime < (diffElapsed-20)) {
+                final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
+                mHistoryAddTmp.setTo(mHistoryLastWritten);
+                mHistoryAddTmp.wakelockTag = null;
+                mHistoryAddTmp.wakeReasonTag = null;
+                mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+                mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+                addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+            }
+        }
+        mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+        mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
+        mTrackRunningHistoryUptime = uptimeMs;
+        addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+    }
+
+    void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
 
         if (!USE_OLD_HISTORY) {
             return;
@@ -2080,25 +2158,28 @@
         // into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
                 && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
-                && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
+                && ((mHistoryEnd.states^cur.states)&mChangedStates) == 0
+                && ((mHistoryEnd.states2^cur.states2)&mChangedStates2) == 0) {
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
                     && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
-                    && mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
+                    && mHistoryLastEnd.sameNonEvent(cur)) {
                 mHistoryLastEnd.next = null;
                 mHistoryEnd.next = mHistoryCache;
                 mHistoryCache = mHistoryEnd;
                 mHistoryEnd = mHistoryLastEnd;
                 mHistoryLastEnd = null;
             } else {
-                mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
-                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+                mChangedStates |= mHistoryEnd.states^cur.states;
+                mChangedStates2 |= mHistoryEnd.states^cur.states2;
+                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur);
             }
             return;
         }
 
         mChangedStates = 0;
+        mChangedStates2 = 0;
 
         if (mNumHistoryItems == MAX_HISTORY_ITEMS
                 || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
@@ -2111,9 +2192,9 @@
             // Once we've reached the maximum maximum number of items, we only
             // record changes to the battery level.
             if (mHistoryEnd != null && mHistoryEnd.batteryLevel
-                    == mHistoryCur.batteryLevel &&
+                    == cur.batteryLevel &&
                     (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
-                            || ((mHistoryEnd.states^mHistoryCur.states)
+                            || ((mHistoryEnd.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
@@ -2128,17 +2209,18 @@
         mHistoryCur.eventTag = mHistoryCur.localEventTag;
         mHistoryCur.eventTag.string = name;
         mHistoryCur.eventTag.uid = uid;
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
+        // XXX should be calling addHistoryRecordLocked()?
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
     }
 
-    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
+    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
         HistoryItem rec = mHistoryCache;
         if (rec != null) {
             mHistoryCache = rec.next;
         } else {
             rec = new HistoryItem();
         }
-        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
+        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
 
         addHistoryRecordLocked(rec);
     }
@@ -2168,7 +2250,8 @@
 
         mHistoryBaseTime = 0;
         mLastHistoryElapsedRealtime = 0;
-        mLastHistoryUptime = 0;
+        mTrackRunningHistoryElapsedRealtime = 0;
+        mTrackRunningHistoryUptime = 0;
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
@@ -2284,6 +2367,7 @@
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
+            aggregateLastWakeupUptimeLocked(uptime);
             if (mWakeLockNesting == 0) {
                 mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2372,14 +2456,26 @@
         }
     }
 
-    public void noteWakeupReasonLocked(int irq, String reason) {
+    void aggregateLastWakeupUptimeLocked(long uptimeMs) {
+        if (mLastWakeupReason != null) {
+            long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
+            LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
+            timer.addCountLocked(deltaUptime);
+            mLastWakeupReason = null;
+        }
+    }
+
+    public void noteWakeupReasonLocked(String reason) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
                 + Integer.toHexString(mHistoryCur.states));
+        aggregateLastWakeupUptimeLocked(uptime);
         mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
         mHistoryCur.wakeReasonTag.string = reason;
-        mHistoryCur.wakeReasonTag.uid = irq;
+        mHistoryCur.wakeReasonTag.uid = 0;
+        mLastWakeupReason = reason;
+        mLastWakeupUptimeMs = uptime;
         addHistoryRecordLocked(elapsedRealtime, uptime);
     }
 
@@ -2670,13 +2766,28 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active) {
+    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
         if (ConnectivityManager.isNetworkTypeMobile(type)) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             if (mMobileRadioActive != active) {
-                if (active) mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
-                else mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                long realElapsedRealtimeMs;
+                if (active) {
+                    realElapsedRealtimeMs = elapsedRealtime;
+                    mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                } else {
+                    realElapsedRealtimeMs = timestampNs / (1000*1000);
+                    long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+                    if (realElapsedRealtimeMs < lastUpdateTimeMs) {
+                        Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+                                + " is before start time " + lastUpdateTimeMs);
+                        realElapsedRealtimeMs = elapsedRealtime;
+                    } else if (realElapsedRealtimeMs < elapsedRealtime) {
+                        mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+                                - realElapsedRealtimeMs);
+                    }
+                    mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                }
                 if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
                         + Integer.toHexString(mHistoryCur.states));
                 addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -2685,9 +2796,9 @@
                     mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
                     mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
                 } else {
-                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, elapsedRealtime);
-                    mMobileRadioActiveTimer.stopRunningLocked(elapsedRealtime);
-                    mMobileRadioActivePerAppTimer.stopRunningLocked(elapsedRealtime);
+                    mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
+                    mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
+                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
                 }
             }
         }
@@ -2978,7 +3089,7 @@
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
         if (!mVideoOn) {
-            mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -2993,7 +3104,7 @@
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
         if (mVideoOn) {
-            mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3398,6 +3509,10 @@
         return mMobileRadioActiveTimer.getCountLocked(which);
     }
 
+    @Override public long getMobileRadioActiveAdjustedTime(int which) {
+        return mMobileRadioActiveAdjustedTime.getCountLocked(which);
+    }
+
     @Override public long getMobileRadioActiveUnknownTime(int which) {
         return mMobileRadioActiveUnknownTime.getCountLocked(which);
     }
@@ -5458,6 +5573,7 @@
         }
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase);
+        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
         mWifiOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
@@ -5543,9 +5659,9 @@
                 PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
                 pw.println("Histories differ!");
                 pw.println("Old history:");
-                (new HistoryPrinter()).printNextItem(pw, out, now, false);
+                (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
                 pw.println("New history:");
-                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false);
+                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
                 pw.flush();
             }
         }
@@ -5719,6 +5835,7 @@
         }
         mMobileRadioActiveTimer.reset(false);
         mMobileRadioActivePerAppTimer.reset(false);
+        mMobileRadioActiveAdjustedTime.reset(false);
         mMobileRadioActiveUnknownTime.reset(false);
         mMobileRadioActiveUnknownCount.reset(false);
         mWifiOnTimer.reset(false);
@@ -5744,7 +5861,14 @@
             }
             mKernelWakelockStats.clear();
         }
-        
+
+        if (mWakeupReasonStats.size() > 0) {
+            for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
+                mOnBatteryScreenOffTimeBase.remove(timer);
+            }
+            mWakeupReasonStats.clear();
+        }
+
         initDischarge();
 
         clearHistoryLocked();
@@ -5828,9 +5952,10 @@
             mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
                     + Integer.toHexString(mHistoryCur.states));
-            mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(mSecRealtime, mSecUptime, HistoryItem.CMD_CURRENT_TIME);
-            mHistoryCur.currentTime = 0;
+            if (reset) {
+                mRecordingHistory = true;
+                startRecordingHistory(mSecRealtime, mSecUptime, reset);
+            }
             addHistoryRecordLocked(mSecRealtime, mSecUptime);
             mDischargeCurrentLevel = mDischargeUnplugLevel = level;
             if (mScreenOn) {
@@ -5843,9 +5968,6 @@
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
             updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
-            if (reset) {
-                initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
-            }
         } else {
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
@@ -5868,6 +5990,18 @@
         }
     }
 
+    private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+            boolean reset) {
+        mRecordingHistory = true;
+        mHistoryCur.currentTime = System.currentTimeMillis();
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+                mHistoryCur);
+        mHistoryCur.currentTime = 0;
+        if (reset) {
+            initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
+        }
+    }
+
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
@@ -5895,7 +6029,15 @@
             }
             if (onBattery) {
                 mDischargeCurrentLevel = level;
-                mRecordingHistory = true;
+                if (!mRecordingHistory) {
+                    mRecordingHistory = true;
+                    startRecordingHistory(elapsedRealtime, uptime, true);
+                }
+            } else if (level < 96) {
+                if (!mRecordingHistory) {
+                    mRecordingHistory = true;
+                    startRecordingHistory(elapsedRealtime, uptime, true);
+                }
             }
             if (onBattery != mOnBattery) {
                 mHistoryCur.batteryLevel = (byte)level;
@@ -6497,15 +6639,14 @@
         }
 
         if (mHistoryBuffer.dataPosition() > 0) {
+            mRecordingHistory = true;
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             if (USE_OLD_HISTORY) {
-                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
+                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
             }
-            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
-            mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_CURRENT_TIME);
-            mHistoryCur.currentTime = 0;
+            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+            startRecordingHistory(elapsedRealtime, uptime, false);
         }
     }
 
@@ -6681,6 +6822,7 @@
         mMobileRadioActive = false;
         mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
         mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
+        mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
         mWifiOn = false;
@@ -6708,6 +6850,18 @@
             }
         }
 
+        int NWR = in.readInt();
+        if (NWR > 10000) {
+            Slog.w(TAG, "File corrupt: too many wakeup reasons " + NWR);
+            return;
+        }
+        for (int iwr = 0; iwr < NWR; iwr++) {
+            if (in.readInt() != 0) {
+                String reasonName = in.readString();
+                getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+            }
+        }
+
         sNumSpeedSteps = in.readInt();
         if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
             throw new BadParcelableException("Bad speed steps in data: " + sNumSpeedSteps);
@@ -6915,6 +7069,7 @@
         }
         mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
         mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -6933,7 +7088,19 @@
             if (kwlt != null) {
                 out.writeInt(1);
                 out.writeString(ent.getKey());
-                ent.getValue().writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                kwlt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
+        }
+
+        out.writeInt(mWakeupReasonStats.size());
+        for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+            LongSamplingCounter counter = ent.getValue();
+            if (counter != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                counter.writeSummaryFromParcelLocked(out);
             } else {
                 out.writeInt(0);
             }
@@ -7171,6 +7338,7 @@
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase, in);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase,
                 in);
+        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mWifiOn = false;
@@ -7205,12 +7373,22 @@
         for (int ikw = 0; ikw < NKW; ikw++) {
             if (in.readInt() != 0) {
                 String wakelockName = in.readString();
-                in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel
                 SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
                 mKernelWakelockStats.put(wakelockName, kwlt);
             }
         }
 
+        mWakeupReasonStats.clear();
+        int NWR = in.readInt();
+        for (int iwr = 0; iwr < NWR; iwr++) {
+            if (in.readInt() != 0) {
+                String reasonName = in.readString();
+                LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
+                        in);
+                mWakeupReasonStats.put(reasonName, counter);
+            }
+        }
+
         mPartialTimers.clear();
         mFullTimers.clear();
         mWindowTimers.clear();
@@ -7283,6 +7461,7 @@
         }
         mMobileRadioActiveTimer.writeToParcel(out, uSecRealtime);
         mMobileRadioActivePerAppTimer.writeToParcel(out, uSecRealtime);
+        mMobileRadioActiveAdjustedTime.writeToParcel(out);
         mMobileRadioActiveUnknownTime.writeToParcel(out);
         mMobileRadioActiveUnknownCount.writeToParcel(out);
         mWifiOnTimer.writeToParcel(out, uSecRealtime);
@@ -7313,7 +7492,18 @@
                 if (kwlt != null) {
                     out.writeInt(1);
                     out.writeString(ent.getKey());
-                    Timer.writeTimerToParcel(out, kwlt, uSecRealtime);
+                    kwlt.writeToParcel(out, uSecRealtime);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+            out.writeInt(mWakeupReasonStats.size());
+            for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+                LongSamplingCounter counter = ent.getValue();
+                if (counter != null) {
+                    out.writeInt(1);
+                    out.writeString(ent.getKey());
+                    counter.writeToParcel(out);
                 } else {
                     out.writeInt(0);
                 }
@@ -7383,6 +7573,8 @@
             }
             pr.println("*** Mobile network active timer:");
             mMobileRadioActiveTimer.logState(pr, "  ");
+            pr.println("*** Mobile network active adjusted timer:");
+            mMobileRadioActiveAdjustedTime.logState(pr, "  ");
             pr.println("*** Wifi timer:");
             mWifiOnTimer.logState(pr, "  ");
             pr.println("*** WifiRunning timer:");
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 91056f1..9501f92 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -28,6 +28,7 @@
     boolean checkPattern(in String pattern, int userId);
     void setLockPassword(in String password, int userId);
     boolean checkPassword(in String password, int userId);
+    boolean checkVoldPassword(int userId);
     boolean havePattern(int userId);
     boolean havePassword(int userId);
     void removeUser(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 2d79491..e5aaf7e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -313,6 +313,20 @@
     }
 
     /**
+     * Check to see if vold already has the password.
+     * Note that this also clears vold's copy of the password.
+     * @return Whether the vold password matches or not.
+     */
+    public boolean checkVoldPassword() {
+        final int userId = getCurrentOrCallingUserId();
+        try {
+            return getLockSettings().checkVoldPassword(userId);
+        } catch (RemoteException re) {
+            return false;
+        }
+    }
+
+    /**
      * Check to see if a password matches any of the passwords stored in the
      * password history.
      *
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
index f856027..64ce918 100644
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -24,6 +24,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Matrix;
+import android.media.AudioManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -34,7 +35,9 @@
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.animation.DecelerateInterpolator;
+
 import static android.view.animation.AnimationUtils.currentAnimationTimeMillis;
+
 import com.android.internal.R;
 
 
@@ -676,7 +679,7 @@
                 mVibrator = (android.os.Vibrator) getContext()
                         .getSystemService(Context.VIBRATOR_SERVICE);
             }
-            mVibrator.vibrate(duration);
+            mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index aebc4f6..deb0fd7 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -821,7 +822,7 @@
                 mVibrator = (android.os.Vibrator) getContext()
                         .getSystemService(Context.VIBRATOR_SERVICE);
             }
-            mVibrator.vibrate(duration);
+            mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
index 0c5993b..86f14b3 100644
--- a/core/java/com/android/internal/widget/WaveView.java
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -25,6 +25,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
+import android.media.AudioManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -582,7 +583,7 @@
                 mVibrator = (android.os.Vibrator) getContext()
                         .getSystemService(Context.VIBRATOR_SERVICE);
             }
-            mVibrator.vibrate(duration);
+            mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index 93ea5b3..772dc5f 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -30,6 +30,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -564,7 +565,7 @@
                 mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
                 UserHandle.USER_CURRENT) != 0;
         if (mVibrator != null && hapticEnabled) {
-            mVibrator.vibrate(mVibrationDuration);
+            mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index e22d1e8..4648f39 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -31,6 +31,7 @@
 import android.graphics.Canvas;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -599,7 +600,7 @@
                 mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
                 UserHandle.USER_CURRENT) != 0;
         if (mVibrator != null && hapticEnabled) {
-            mVibrator.vibrate(mVibrationDuration);
+            mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index a64d3ba..3a5b566 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -606,7 +606,7 @@
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
-        jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes,
+        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
         jint javaAudioFormat, jboolean isWriteBlocking) {
     //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
     //    offsetInBytes, sizeInBytes);
@@ -623,7 +623,7 @@
         return AUDIOTRACK_ERROR_BAD_VALUE;
     }
 
-    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes,
+    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
             sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
 
     return written;
@@ -922,7 +922,7 @@
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
     {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
     {"native_write_native_bytes",
-                             "(Ljava/lang/Object;IIIIZ)I",
+                             "(Ljava/lang/Object;IIIZ)I",
                                          (void *)android_media_AudioTrack_write_native_bytes},
     {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_write_short},
     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e1efc6..2168bd1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -684,6 +684,12 @@
         android:label="@string/permlab_installLocationProvider"
         android:description="@string/permdesc_installLocationProvider" />
 
+    <!-- Allows HDMI-CEC service to access device and configuration files.
+         @hide This should only be used by HDMI-CEC service.
+    -->
+    <permission android:name="android.permission.HDMI_CEC"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- Allows an application to use location features in hardware,
          such as the geofencing api.
          <p>Not for use by third-party applications. -->
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index dee8705..40c6797 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1207,14 +1207,11 @@
      * In streaming mode, the blocking behavior will depend on the write mode.
      * @param audioData the buffer that holds the data to play, starting at the position reported
      *     by <code>audioData.position()</code>.
-     *     <BR>Note that this method will not update the position in this buffer, therefore when
-     *     writing a loop to write all the data in the buffer, you should increment the
-     *     <code>offsetInBytes</code> parameter at each pass by the amount that was previously
-     *     written for this buffer.
-     * @param offsetInBytes offset to read from in bytes (note this differs from
-     *     <code>audioData.position()</code>).
-     * @param sizeInBytes number of bytes to read (note this differs from
-     *     <code>audioData.remaining()</code>).
+     *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
+     *     have been advanced to reflect the amount of data that was successfully written to
+     *     the AudioTrack.
+     * @param sizeInBytes number of bytes to write.
+     *     <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1224,7 +1221,7 @@
      * @return 0 or a positive number of bytes that were written, or
      *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
      */
-    public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes,
+    public int write(ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode) {
 
         if (mState == STATE_UNINITIALIZED) {
@@ -1237,22 +1234,19 @@
             return ERROR_BAD_VALUE;
         }
 
-        if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
-                || (offsetInBytes + sizeInBytes < 0)    // detect integer overflow
-                || (offsetInBytes + sizeInBytes > audioData.remaining())) {
-            Log.e(TAG, "AudioTrack.write() called with invalid size/offset values");
+        if ( (audioData == null) || (sizeInBytes < 0) || (sizeInBytes > audioData.remaining())) {
+            Log.e(TAG, "AudioTrack.write() called with invalid size (" + sizeInBytes + ") value");
             return ERROR_BAD_VALUE;
         }
 
         int ret = 0;
         if (audioData.isDirect()) {
             ret = native_write_native_bytes(audioData,
-                    audioData.position(),
-                    offsetInBytes, sizeInBytes, mAudioFormat,
+                    audioData.position(), sizeInBytes, mAudioFormat,
                     writeMode == WRITE_BLOCKING);
         } else {
             ret = native_write_byte(NioUtils.unsafeArray(audioData),
-                    NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes,
+                    NioUtils.unsafeArrayOffset(audioData) + audioData.position(),
                     sizeInBytes, mAudioFormat,
                     writeMode == WRITE_BLOCKING);
         }
@@ -1264,6 +1258,10 @@
             mState = STATE_INITIALIZED;
         }
 
+        if (ret > 0) {
+            audioData.position(audioData.position() + ret);
+        }
+
         return ret;
     }
 
@@ -1476,7 +1474,7 @@
                                                 int offsetInShorts, int sizeInShorts, int format);
 
     private native final int native_write_native_bytes(Object audioData,
-            int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking);
+            int positionInBytes, int sizeInBytes, int format, boolean blocking);
 
     private native final int native_reload_static();
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index 31e806c..3fc562c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -979,6 +979,13 @@
             return;
         }
 
+        if (mLockPatternUtils.checkVoldPassword()) {
+            if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
+            // Without this, settings is not enabled until the lock screen first appears
+            hideLocked();
+            return;
+        }
+
         if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
         showLocked(options);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index e1a4bb2..48a6522 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -22,6 +22,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.Vibrator;
 import android.util.Log;
 import android.view.Gravity;
@@ -605,7 +606,7 @@
             mVibrator = (android.os.Vibrator)
                     mContext.getSystemService(Context.VIBRATOR_SERVICE);
         }
-        mVibrator.vibrate(duration);
+        mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index b096ead..09aa60f 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.media.AudioManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -207,7 +208,8 @@
                 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
             Resources res = context.getResources();
             Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-            vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
+            vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration),
+                    AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index c5e325e..cdbee82 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -29,45 +29,73 @@
 import android.os.HandlerThread;
 import android.os.UserHandle;
 import android.util.LruCache;
+import android.util.Pair;
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 
 /** A bitmap load queue */
 class TaskResourceLoadQueue {
     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
+    ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
+            new ConcurrentHashMap<Task.TaskKey, Boolean>();
 
-    Task nextTask() {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
-        return mQueue.poll();
-    }
+    static final Boolean sFalse = new Boolean(false);
 
-    void addTask(Task t) {
+    /** Adds a new task to the load queue */
+    void addTask(Task t, boolean forceLoad) {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
         if (!mQueue.contains(t)) {
             mQueue.add(t);
         }
+        if (forceLoad) {
+            mForceLoadSet.put(t.key, new Boolean(true));
+        }
         synchronized(this) {
             notifyAll();
         }
     }
 
+    /**
+     * Retrieves the next task from the load queue, as well as whether we want that task to be
+     * force reloaded.
+     */
+    Pair<Task, Boolean> nextTask() {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
+        Task task = mQueue.poll();
+        Boolean forceLoadTask = null;
+        if (task != null) {
+            forceLoadTask = mForceLoadSet.remove(task.key);
+        }
+        if (forceLoadTask == null) {
+            forceLoadTask = sFalse;
+        }
+        return new Pair<Task, Boolean>(task, forceLoadTask);
+    }
+
+    /** Removes a task from the load queue */
     void removeTask(Task t) {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
         mQueue.remove(t);
+        mForceLoadSet.remove(t.key);
     }
 
+    /** Clears all the tasks from the load queue */
     void clearTasks() {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
         mQueue.clear();
+        mForceLoadSet.clear();
     }
 
+    /** Returns whether the load queue is empty */
     boolean isEmpty() {
         return mQueue.isEmpty();
     }
@@ -147,16 +175,19 @@
                 }
             } else {
                 // Load the next item from the queue
-                final Task t = mLoadQueue.nextTask();
+                Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
+                final Task t = nextTaskData.first;
+                final boolean forceLoadTask = nextTaskData.second;
                 if (t != null) {
                     try {
                         Drawable loadIcon = mIconCache.get(t.key);
                         Bitmap loadThumbnail = mThumbnailCache.get(t.key);
                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                 "  [TaskResourceLoader|load]",
-                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail);
+                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
+                                        " forceLoad: " + forceLoadTask);
                         // Load the icon
-                        if (loadIcon == null) {
+                        if (loadIcon == null || forceLoadTask) {
                             PackageManager pm = mContext.getPackageManager();
                             ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
                                     PackageManager.GET_META_DATA);
@@ -172,7 +203,7 @@
                             }
                         }
                         // Load the thumbnail
-                        if (loadThumbnail == null) {
+                        if (loadThumbnail == null || forceLoadTask) {
                             ActivityManager am = (ActivityManager)
                                     mContext.getSystemService(Context.ACTIVITY_SERVICE);
                             Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
@@ -197,7 +228,7 @@
                             mMainThreadHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
-                                    t.notifyTaskDataLoaded(newThumbnail, newIcon);
+                                    t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask);
                                 }
                             });
                         }
@@ -329,9 +360,11 @@
     /** Reload the set of recent tasks */
     SpaceNode reload(Context context, int preloadCount) {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
+        ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
         TaskStack stack = new TaskStack(context);
         SpaceNode root = new SpaceNode(context);
         root.setStack(stack);
+
         try {
             long t1 = System.currentTimeMillis();
 
@@ -387,6 +420,12 @@
                     // Load the icon (if possible and not the foremost task, from the cache)
                     if (!isForemostTask) {
                         task.icon = mIconCache.get(task.key);
+
+                        if (task.icon != null) {
+                            // Even though we get things from the cache, we should update them if
+                            // they've changed in the bg
+                            tasksToForceLoad.add(task);
+                        }
                     }
                     if (task.icon == null) {
                         task.icon = info.loadIcon(pm);
@@ -400,6 +439,12 @@
                     // Load the thumbnail (if possible and not the foremost task, from the cache)
                     if (!isForemostTask) {
                         task.thumbnail = mThumbnailCache.get(task.key);
+
+                        if (task.thumbnail != null) {
+                            // Even though we get things from the cache, we should update them if
+                            // they've changed in the bg
+                            tasksToForceLoad.add(task);
+                        }
                     }
                     if (task.thumbnail == null) {
                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
@@ -451,6 +496,11 @@
         // Start the task loader
         mLoader.start(context);
 
+        // Add all the tasks that we are force/re-loading
+        for (Task t : tasksToForceLoad) {
+            mLoadQueue.addTask(t, true);
+        }
+
         return root;
     }
 
@@ -473,9 +523,9 @@
             requiresLoad = true;
         }
         if (requiresLoad) {
-            mLoadQueue.addTask(t);
+            mLoadQueue.addTask(t, false);
         }
-        t.notifyTaskDataLoaded(thumbnail, icon);
+        t.notifyTaskDataLoaded(thumbnail, icon, false);
     }
 
     /** Releases the task resource data back into the pool. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0c3c528..edcf9b2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -29,7 +29,7 @@
     /* Task callbacks */
     public interface TaskCallbacks {
         /* Notifies when a task has been bound */
-        public void onTaskDataLoaded();
+        public void onTaskDataLoaded(boolean reloadingTaskData);
         /* Notifies when a task has been unbound */
         public void onTaskDataUnloaded();
     }
@@ -84,11 +84,11 @@
     }
 
     /** Notifies the callback listeners that this task has been loaded */
-    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon) {
+    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon, boolean reloadingTaskData) {
         this.icon = icon;
         this.thumbnail = thumbnail;
         if (mCb != null) {
-            mCb.onTaskDataLoaded();
+            mCb.onTaskDataLoaded(reloadingTaskData);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 62cf394..2b27a06 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -881,6 +881,13 @@
                 }
                 break;
             }
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mActivePointerId = ev.getPointerId(index);
+                mLastMotionX = (int) ev.getX(index);
+                mLastMotionY = (int) ev.getY(index);
+                break;
+            }
             case MotionEvent.ACTION_MOVE: {
                 if (mActivePointerId == INACTIVE_POINTER_ID) break;
 
@@ -957,6 +964,19 @@
                 recycleVelocityTracker();
                 break;
             }
+            case MotionEvent.ACTION_POINTER_UP: {
+                int pointerIndex = ev.getActionIndex();
+                int pointerId = ev.getPointerId(pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    // Select a new active pointer id and reset the motion state
+                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
+                    mLastMotionX = (int) ev.getX(newPointerIndex);
+                    mLastMotionY = (int) ev.getY(newPointerIndex);
+                    mVelocityTracker.clear();
+                }
+                break;
+            }
             case MotionEvent.ACTION_CANCEL: {
                 if (mIsScrolling) {
                     // Disable HW layers
@@ -1006,10 +1026,6 @@
         Task task = tv.getTask();
         Activity activity = (Activity) mSv.getContext();
 
-        // We have to disable the listener to ensure that we
-        // don't hit this again
-        tv.animate().setListener(null);
-
         // Remove the task from the view
         mSv.mStack.removeTask(task);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index f411717..e6dce37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -65,10 +65,7 @@
 
             setImageBitmap(t.thumbnail);
             if (animate) {
-                setAlpha(0f);
-                animate().alpha(1f)
-                        .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
-                        .start();
+                // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
             }
         }
     }
@@ -143,10 +140,7 @@
         if (t.icon != null) {
             setImageDrawable(t.icon);
             if (animate) {
-                setAlpha(0f);
-                animate().alpha(1f)
-                        .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
-                        .start();
+                // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
             }
         }
     }
@@ -311,6 +305,7 @@
                     .translationY(0)
                     .setStartDelay(235)
                     .setDuration(Constants.Values.TaskView.Animation.TaskIconOnEnterDuration)
+                    .withLayer()
                     .start();
         }
     }
@@ -333,13 +328,8 @@
                 .setStartDelay(0)
                 .setDuration(Constants.Values.TaskView.Animation.TaskIconOnLeavingDuration)
                 .setInterpolator(new DecelerateInterpolator())
-                .setListener(
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            r.run();
-                        }
-                    })
+                .withLayer()
+                .withEndAction(r)
                 .start();
         }
     }
@@ -379,10 +369,10 @@
     }
 
     @Override
-    public void onTaskDataLoaded() {
+    public void onTaskDataLoaded(boolean reloadingTaskData) {
         // Bind each of the views to the new task data
-        mThumbnailView.rebindToTask(mTask, false);
-        mIconView.rebindToTask(mTask, false);
+        mThumbnailView.rebindToTask(mTask, reloadingTaskData);
+        mIconView.rebindToTask(mTask, reloadingTaskData);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index bd36128..68c8364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -111,7 +111,7 @@
 
     protected static final boolean ENABLE_HEADS_UP = true;
     // scores above this threshold should be displayed in heads up mode.
-    protected static final int INTERRUPTION_THRESHOLD = 11;
+    protected static final int INTERRUPTION_THRESHOLD = 10;
     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
     // Should match the value in PhoneWindowManager
@@ -757,6 +757,7 @@
 
     protected void toggleRecentsActivity() {
         if (mRecents != null) {
+            sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
         }
     }
@@ -1521,17 +1522,8 @@
     // A: Almost none! Only things coming from the system (package is "android") that also
     // have special "kind" tags marking them as relevant for setup (see below).
     protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
-        if ("android".equals(sbn.getPackageName())) {
-            if (sbn.getNotification().kind != null) {
-                for (String aKind : sbn.getNotification().kind) {
-                    // IME switcher, created by InputMethodManagerService
-                    if ("android.system.imeswitcher".equals(aKind)) return true;
-                    // OTA availability & errors, created by SystemUpdateService
-                    if ("android.system.update".equals(aKind)) return true;
-                }
-            }
-        }
-        return false;
+        return "android".equals(sbn.getPackageName())
+                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
     }
 
     public boolean inKeyguardRestrictedInputMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9540bd4..91325ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -49,6 +49,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2713,7 +2714,7 @@
     void vibrate() {
         android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
                 Context.VIBRATOR_SERVICE);
-        vib.vibrate(250);
+        vib.vibrate(250, AudioManager.STREAM_SYSTEM);
     }
 
     Runnable mStartTracing = new Runnable() {
diff --git a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
index 1622742..2a0188a 100644
--- a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
+++ b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
@@ -20,6 +20,7 @@
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     style="?android:actionButtonStyle"
+    android:id="@+id/set_wallpaper_button"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
     <TextView style="?android:actionBarTabTextStyle"
diff --git a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
index cdc5cdc..764156d 100644
--- a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
+++ b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
@@ -245,6 +245,9 @@
             try {
                 ei.readExif(mPath);
                 return true;
+            } catch (NullPointerException e) {
+                Log.w("BitmapRegionTileSource", "reading exif failed", e);
+                return false;
             } catch (IOException e) {
                 Log.w("BitmapRegionTileSource", "getting decoder failed", e);
                 return false;
@@ -311,6 +314,9 @@
             } catch (IOException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return false;
+            } catch (NullPointerException e) {
+                Log.e("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e);
+                return false;
             } finally {
                 Utils.closeSilently(is);
             }
diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
index e45b98c..d6c0c99 100644
--- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
+++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
@@ -75,6 +75,7 @@
 
     protected CropView mCropView;
     protected Uri mUri;
+    private View mSetWallpaperButton;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -111,10 +112,12 @@
                         cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
                     }
                 });
+        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
 
         // Load image in background
         final BitmapRegionTileSource.UriBitmapSource bitmapSource =
                 new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+        mSetWallpaperButton.setVisibility(View.INVISIBLE);
         Runnable onLoad = new Runnable() {
             public void run() {
                 if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
@@ -122,6 +125,8 @@
                             getString(R.string.wallpaper_load_fail),
                             Toast.LENGTH_LONG).show();
                     finish();
+                } else {
+                    mSetWallpaperButton.setVisibility(View.VISIBLE);
                 }
             }
         };
@@ -136,7 +141,21 @@
         final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
             protected Void doInBackground(Void...args) {
                 if (!isCancelled()) {
-                    bitmapSource.loadInBackground();
+                    try {
+                        bitmapSource.loadInBackground();
+                    } catch (SecurityException securityException) {
+                        if (isDestroyed()) {
+                            // Temporarily granted permissions are revoked when the activity
+                            // finishes, potentially resulting in a SecurityException here.
+                            // Even though {@link #isDestroyed} might also return true in different
+                            // situations where the configuration changes, we are fine with
+                            // catching these cases here as well.
+                            cancel(false);
+                        } else {
+                            // otherwise it had a different cause and we throw it further
+                            throw securityException;
+                        }
+                    }
                 }
                 return null;
             }
@@ -271,6 +290,9 @@
             }
         } catch (IOException e) {
             Log.w(LOGTAG, "Getting exif data failed", e);
+        } catch (NullPointerException e) {
+            // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
+            Log.w(LOGTAG, "Getting exif data failed", e);
         } finally {
             Utils.closeSilently(bis);
             Utils.closeSilently(is);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9de3efe..41ffdc1 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -103,6 +103,7 @@
     private long mNextNonWakeup;
     int mBroadcastRefCount = 0;
     PowerManager.WakeLock mWakeLock;
+    boolean mLastWakeLockUnimportantForLogging;
     ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
     final AlarmHandler mHandler = new AlarmHandler();
     ClockReceiver mClockReceiver;
@@ -1345,17 +1346,21 @@
      */
     void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, boolean first) {
         try {
-            mWakeLock.setUnimportantForLogging(pi == mTimeTickSender);
+            final boolean unimportant = pi == mTimeTickSender;
+            mWakeLock.setUnimportantForLogging(unimportant);
             if (ws != null) {
-                if (first) {
+                if (first || mLastWakeLockUnimportantForLogging) {
                     mWakeLock.setHistoryTag(pi.getTag(
                             type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
                                     ? "*walarm*:" : "*alarm*:"));
                 } else {
                     mWakeLock.setHistoryTag(null);
                 }
+                mLastWakeLockUnimportantForLogging = unimportant;
                 mWakeLock.setWorkSource(ws);
                 return;
+            } else {
+                mLastWakeLockUnimportantForLogging = false;
             }
 
             final int uid = ActivityManagerNative.getDefault()
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index af8a6e7..31822e7 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -637,7 +637,8 @@
         mImeSwitcherNotification.vibrate = null;
 
         // Tag this notification specially so SystemUI knows it's important
-        mImeSwitcherNotification.kind = new String[] { "android.system.imeswitcher" };
+        mImeSwitcherNotification.extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
+        mImeSwitcherNotification.category = Notification.CATEGORY_SYSTEM;
 
         Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index fe814fc..19e8083 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -30,7 +30,11 @@
 import android.database.sqlite.SQLiteStatement;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.storage.IMountService;
+import android.os.ServiceManager;
+import android.os.storage.StorageManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -79,6 +83,7 @@
 
     private final Context mContext;
     private LockPatternUtils mLockPatternUtils;
+    private boolean mFirstCallToVold;
 
     public LockSettingsService(Context context) {
         mContext = context;
@@ -86,6 +91,7 @@
         mOpenHelper = new DatabaseHelper(mContext);
 
         mLockPatternUtils = new LockPatternUtils(context);
+        mFirstCallToVold = true;
     }
 
     public void systemReady() {
@@ -347,6 +353,33 @@
     }
 
     @Override
+    public boolean checkVoldPassword(int userId) throws RemoteException {
+        if (!mFirstCallToVold) {
+            return false;
+        }
+        mFirstCallToVold = false;
+
+        checkPasswordReadPermission(userId);
+
+        // There's no guarantee that this will safely connect, but if it fails
+        // we will simply show the lock screen when we shouldn't, so relatively
+        // benign. There is an outside chance something nasty would happen if
+        // this service restarted before vold stales out the password in this
+        // case. The nastiness is limited to not showing the lock screen when
+        // we should, within the first minute of decrypting the phone if this
+        // service can't connect to vold, it restarts, and then the new instance
+        // does successfully connect.
+        final IMountService service = getMountService();
+        String password = service.getPassword();
+        service.clearPassword();
+        if (service.getPasswordType() == StorageManager.CRYPT_TYPE_PATTERN) {
+            return checkPattern(password, userId);
+        } else {
+            return checkPassword(password, userId);
+        }
+    }
+
+    @Override
     public void removeUser(int userId) {
         checkWritePermission(userId);
 
@@ -524,4 +557,12 @@
         Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
         Secure.LOCK_SCREEN_OWNER_INFO
     };
+
+    private IMountService getMountService() {
+        final IBinder service = ServiceManager.getService("mount");
+        if (service != null) {
+            return IMountService.Stub.asInterface(service);
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cd74fed..b6e0d5f 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -74,6 +74,7 @@
 import com.google.android.collect.Maps;
 
 import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.DecoderException;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
@@ -573,6 +574,14 @@
         }
     }
 
+    private boolean isReady() {
+        try {
+            return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
     private void handleSystemReady() {
         // Snapshot current volume states since it's not safe to call into vold
         // while holding locks.
@@ -2081,6 +2090,19 @@
         return new String(Hex.encodeHex(bytes));
     }
 
+    private String fromHex(String hexPassword) {
+        if (hexPassword == null) {
+            return null;
+        }
+
+        try {
+            byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
+            return new String(bytes, StandardCharsets.UTF_8);
+        } catch (DecoderException e) {
+            return null;
+        }
+    }
+
     @Override
     public int decryptStorage(String password) {
         if (TextUtils.isEmpty(password)) {
@@ -2230,6 +2252,35 @@
     }
 
     @Override
+    public String getPassword() throws RemoteException {
+        if (!isReady()) {
+            return new String();
+        }
+
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("cryptfs", "getpw");
+            return fromHex(event.getMessage());
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void clearPassword() throws RemoteException {
+        if (!isReady()) {
+            return;
+        }
+
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("cryptfs", "clearpw");
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
     public int mkdirs(String callingPkg, String appPath) {
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
         final UserEnvironment userEnv = new UserEnvironment(userId);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a09d605..5273cec 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -348,7 +348,7 @@
      */
     private void notifyInterfaceClassActivity(int type, boolean active, long tsNanos) {
         try {
-            getBatteryStats().noteDataConnectionActive(type, active);
+            getBatteryStats().noteDataConnectionActive(type, active, tsNanos);
         } catch (RemoteException e) {
         }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 52f9aa9..aa756a1 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -362,7 +362,7 @@
         } catch (RemoteException e) {
         }
         if (vib.mTimeout != 0) {
-            doVibratorOn(vib.mTimeout, vib.mUid);
+            doVibratorOn(vib.mTimeout, vib.mUid, vib.mStreamHint);
             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
         } else {
             // mThread better be null here. doCancelVibrate should always be
@@ -481,7 +481,7 @@
         return vibratorExists();
     }
 
-    private void doVibratorOn(long millis, int uid) {
+    private void doVibratorOn(long millis, int uid, int streamHint) {
         synchronized (mInputDeviceVibrators) {
             try {
                 mBatteryStatsService.noteVibratorOn(uid, millis);
@@ -491,7 +491,7 @@
             final int vibratorCount = mInputDeviceVibrators.size();
             if (vibratorCount != 0) {
                 for (int i = 0; i < vibratorCount; i++) {
-                    mInputDeviceVibrators.get(i).vibrate(millis);
+                    mInputDeviceVibrators.get(i).vibrate(millis, streamHint);
                 }
             } else {
                 vibratorOn(millis);
@@ -554,6 +554,7 @@
                 final int len = pattern.length;
                 final int repeat = mVibration.mRepeat;
                 final int uid = mVibration.mUid;
+                final int streamHint = mVibration.mStreamHint;
                 int index = 0;
                 long duration = 0;
 
@@ -574,7 +575,7 @@
                         // duration is saved for delay() at top of loop
                         duration = pattern[index++];
                         if (duration > 0) {
-                            VibratorService.this.doVibratorOn(duration, uid);
+                            VibratorService.this.doVibratorOn(duration, uid, streamHint);
                         }
                     } else {
                         if (repeat < 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5500b9d..239e423 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6820,7 +6820,7 @@
             }
         }
 
-        if (pending == null && receiver != null) {
+        if (pending.pendingRecords.isEmpty() && receiver != null) {
             // In this case all thumbnails were available and the client
             // is being asked to be told when the remaining ones come in...
             // which is unusually, since the top-most currently running
@@ -6895,6 +6895,21 @@
                     rti.stackId = tr.stack.mStackId;
                     rti.userId = tr.userId;
 
+                    final ArrayList<ActivityRecord> activities = tr.mActivities;
+                    int numSet = 0;
+                    for (int activityNdx = activities.size() - 1; activityNdx >= 0 && numSet < 2;
+                            --activityNdx) {
+                        final ActivityRecord r = activities.get(activityNdx);
+                        if (rti.activityLabel == null && r.recentsLabel != null) {
+                            rti.activityLabel = r.recentsLabel;
+                            ++numSet;
+                        }
+                        if (rti.activityIcon == null && r.recentsIcon != null) {
+                            rti.activityIcon = r.recentsIcon;
+                            ++numSet;
+                        }
+                    }
+
                     if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
                         // Check whether this activity is currently available.
                         try {
@@ -6960,6 +6975,26 @@
     }
 
     @Override
+    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) {
+        synchronized (this) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.recentsLabel = recentsLabel.toString();
+            }
+        }
+    }
+
+    @Override
+    public void setRecentsIcon(IBinder token, Bitmap recentsIcon) {
+        synchronized (this) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.recentsIcon = recentsIcon;
+            }
+        }
+    }
+
+    @Override
     public boolean removeSubTask(int taskId, int subTaskIndex) {
         synchronized (this) {
             enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5d23fc3..645a5e8 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -148,6 +148,9 @@
     boolean mStartingWindowShown = false;
     ActivityContainer mInitialActivityContainer;
 
+    String recentsLabel;
+    Bitmap recentsIcon;
+
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
         pw.print(prefix); pw.print("packageName="); pw.print(packageName);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index fa4a9d1..0ddb827 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -248,10 +248,10 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active) {
+    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteDataConnectionActive(type, active);
+            mStats.noteDataConnectionActive(type, active, timestampNs);
         }
     }
 
@@ -570,9 +570,12 @@
                 int num;
                 while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
                     synchronized (mStats) {
-                        for (int i=0; i<num; i++) {
-                            //Slog.i(TAG, "Wakeup: irq #" + mIrqs[i] + " reason=" + mReasons[i]);
-                            mStats.noteWakeupReasonLocked(mIrqs[i], mReasons[i]);
+                        if (num > 0) {
+                            for (int i=0; i<num; i++) {
+                                mStats.noteWakeupReasonLocked(mReasons[i]);
+                            }
+                        } else {
+                            mStats.noteWakeupReasonLocked("unknown");
                         }
                     }
                 }
@@ -653,7 +656,7 @@
                     dumpHelp(pw);
                     return;
                 } else if ("-a".equals(arg)) {
-                    // fall through
+                    flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
                     pw.println("Unknown option: " + arg);
                     dumpHelp(pw);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e43dea9..026fd296 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2450,7 +2450,7 @@
                             Log.v(TAG, "canceling and rescheduling sync since an initialization "
                                     + "takes higher priority, " + conflict);
                         }
-                    } else if (candidate.expedited && !conflict.mSyncOperation.expedited
+                    } else if (candidate.isExpedited() && !conflict.mSyncOperation.isExpedited()
                             && (candidateIsInitialization
                                 == conflict.mSyncOperation.isInitialization())) {
                         toReschedule = conflict;
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 5233014..9a4abce 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -65,17 +65,18 @@
     /** Why this sync was kicked off. {@link #REASON_NAMES} */
     public final int reason;
     /** Where this sync was initiated. */
-    public int syncSource;
+    public final int syncSource;
     public final boolean allowParallelSyncs;
-    public Bundle extras;
     public final String key;
-    public boolean expedited;
+    /** Internal boolean to avoid reading a bundle everytime we want to compare operations. */
+    private final boolean expedited;
+    public Bundle extras;
     /** Bare-bones version of this operation that is persisted across reboots. */
     public SyncStorageEngine.PendingOperation pendingOperation;
     /** Elapsed real time in millis at which to run this sync. */
     public long latestRunTime;
     /** Set by the SyncManager in order to delay retries. */
-    public Long backoff;
+    public long backoff;
     /** Specified by the adapter to delay subsequent sync operations. */
     public long delayUntil;
     /**
@@ -92,61 +93,58 @@
     public SyncOperation(Account account, int userId, int reason, int source, String provider,
             Bundle extras, long runTimeFromNow, long flexTime, long backoff,
             long delayUntil, boolean allowParallelSyncs) {
-        this.target = new SyncStorageEngine.EndPoint(account, provider, userId);
-        this.reason = reason;
-        this.allowParallelSyncs = allowParallelSyncs;
-        this.key = initialiseOperation(this.target, source, extras, runTimeFromNow, flexTime,
-                backoff, delayUntil);
+        this(new SyncStorageEngine.EndPoint(account, provider, userId),
+                reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
+                allowParallelSyncs);
     }
 
     public SyncOperation(ComponentName service, int userId, int reason, int source,
             Bundle extras, long runTimeFromNow, long flexTime, long backoff,
             long delayUntil) {
-        this.target = new SyncStorageEngine.EndPoint(service, userId);
-        // Default to true for sync service. The service itself decides how to handle this.
-        this.allowParallelSyncs = true;
+        this(new SyncStorageEngine.EndPoint(service, userId), reason, source, extras,
+                runTimeFromNow, flexTime, backoff, delayUntil, true /* allowParallelSyncs */);
+    }
+
+    private SyncOperation(SyncStorageEngine.EndPoint info, int reason, int source, Bundle extras,
+            long runTimeFromNow, long flexTime, long backoff, long delayUntil,
+            boolean allowParallelSyncs) {
+        this.target = info;
         this.reason = reason;
-        this.key =
-                initialiseOperation(this.target,
-                        source, extras, runTimeFromNow, flexTime, backoff, delayUntil);
-    }
-
-    /** Used to reschedule a sync at a new point in time. */
-    SyncOperation(SyncOperation other, long newRunTimeFromNow) {
-        this.target = other.target;
-        this.reason = other.reason;
-        this.expedited = other.expedited;
-        this.allowParallelSyncs = other.allowParallelSyncs;
-        // re-use old flex, but only 
-        long newFlexTime = Math.min(other.flexTime, newRunTimeFromNow);
-        this.key =
-                initialiseOperation(this.target,
-                    other.syncSource, other.extras,
-                    newRunTimeFromNow /* runTimeFromNow*/,
-                    newFlexTime /* flexTime */,
-                    other.backoff,
-                    0L /* delayUntil */);
-    }
-
-    private String initialiseOperation(SyncStorageEngine.EndPoint info, int source, Bundle extras,
-            long runTimeFromNow, long flexTime, long backoff, long delayUntil) {
         this.syncSource = source;
         this.extras = new Bundle(extras);
         cleanBundle(this.extras);
         this.delayUntil = delayUntil;
         this.backoff = backoff;
+        this.allowParallelSyncs = allowParallelSyncs;
         final long now = SystemClock.elapsedRealtime();
-        if (runTimeFromNow < 0 || isExpedited()) {
+        // Set expedited based on runTimeFromNow. The SyncManager specifies whether the op is
+        // expedited (Not done solely based on bundle).
+        if (runTimeFromNow < 0) {
             this.expedited = true;
+            // Sanity check: Will always be true.
+            if (!this.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
+                this.extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+            }
             this.latestRunTime = now;
             this.flexTime = 0;
         } else {
             this.expedited = false;
+            this.extras.remove(ContentResolver.SYNC_EXTRAS_EXPEDITED);
             this.latestRunTime = now + runTimeFromNow;
             this.flexTime = flexTime;
         }
         updateEffectiveRunTime();
-        return toKey(info, this.extras);
+        this.key = toKey(info, this.extras);
+    }
+
+    /** Used to reschedule a sync at a new point in time. */
+    public SyncOperation(SyncOperation other, long newRunTimeFromNow) {
+        this(other.target, other.reason, other.syncSource, new Bundle(other.extras),
+                newRunTimeFromNow,
+                0L /* In back-off so no flex */,
+                other.backoff,
+                other.delayUntil,
+                other.allowParallelSyncs);
     }
 
     public boolean matchesAuthority(SyncOperation other) {
@@ -264,7 +262,7 @@
     }
 
     public boolean isExpedited() {
-        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) || expedited;
+        return expedited;
     }
 
     public boolean ignoreBackoff() {
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
index 5d93882..587de1c 100644
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ b/services/core/java/com/android/server/content/SyncQueue.java
@@ -76,12 +76,11 @@
                 operationToAdd = new SyncOperation(
                         info.account, info.userId, op.reason, op.syncSource, info.provider,
                         op.extras,
-                        0 /* delay */,
+                        op.expedited ? -1 : 0 /* delay */,
                         0 /* flex */,
-                        backoff != null ? backoff.first : 0,
+                        backoff != null ? backoff.first : 0L,
                         mSyncStorageEngine.getDelayUntilTime(info),
                         syncAdapterInfo.type.allowParallelSyncs());
-                operationToAdd.expedited = op.expedited;
                 operationToAdd.pendingOperation = op;
                 add(operationToAdd, op);
             } else if (info.target_service) {
@@ -96,11 +95,10 @@
                 operationToAdd = new SyncOperation(
                         info.service, info.userId, op.reason, op.syncSource,
                         op.extras,
-                        0 /* delay */,
+                        op.expedited ? -1 : 0 /* delay */,
                         0 /* flex */,
                         backoff != null ? backoff.first : 0,
                         mSyncStorageEngine.getDelayUntilTime(info));
-                operationToAdd.expedited = op.expedited;
                 operationToAdd.pendingOperation = op;
                 add(operationToAdd, op);
             }
@@ -129,7 +127,6 @@
         if (existingOperation != null) {
             boolean changed = false;
             if (operation.compareTo(existingOperation) <= 0 ) {
-                existingOperation.expedited = operation.expedited;
                 long newRunTime =
                         Math.min(existingOperation.latestRunTime, operation.latestRunTime);
                 // Take smaller runtime.
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 5df7fc6..3a2e4db 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1088,7 +1088,7 @@
             }
 
             pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras,
-                    op.expedited);
+                    op.isExpedited());
             mPendingOperations.add(pop);
             appendPendingOperationLocked(pop);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
new file mode 100644
index 0000000..9916435
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
@@ -0,0 +1,170 @@
+/*
+ * 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 com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CecDevice class represents a CEC logical device characterized
+ * by its device type. A physical device can contain the functions of
+ * more than one logical device, in which case it should create
+ * as many logical devices as necessary.
+ *
+ * <p>Note that if a physical device has multiple instances of a particular
+ * functionality, it should advertize only one instance. For instance, if
+ * a device has multiple tuners, it should only expose one for control
+ * via CEC. In this case, it is up to the device itself to manage multiple tuners.
+ *
+ * <p>The version of HDMI-CEC protocol supported in this class is 1.3a.
+ *
+ * <p>Declared as package-private, accessed by HdmiCecService only.
+ */
+final class HdmiCecDevice {
+    private static final String TAG = "HdmiCecDevice";
+
+    private final int mType;
+
+    // List of listeners to the message/event coming to the device.
+    private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>();
+    private final Binder mBinder = new Binder();
+
+    private String mName;
+    private boolean mIsActiveSource;
+
+    /**
+     * Constructor.
+     */
+    public HdmiCecDevice(int type) {
+        mType = type;
+        mIsActiveSource = false;
+    }
+
+    /**
+     * Return the binder token that identifies this instance.
+     */
+    public Binder getToken() {
+        return mBinder;
+    }
+
+    /**
+     * Return the type of this device.
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Set the name of the device. The name will be transferred via the message
+     * &lt;Set OSD Name&gt; to other HDMI-CEC devices connected through HDMI
+     * cables and shown on TV screen to identify the devicie.
+     *
+     * @param name name of the device
+     */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    /**
+     * Return the name of this device.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Register a listener to be invoked when events occur.
+     *
+     * @param listener the listern that will run
+     */
+    public void addListener(IHdmiCecListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Remove the listener that was previously registered.
+     *
+     * @param listener IHdmiCecListener instance to be removed
+     */
+    public void removeListener(IHdmiCecListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Indicate if the device has listeners.
+     *
+     * @return true if there are listener instances for this device
+     */
+    public boolean hasListener() {
+        return !mListeners.isEmpty();
+    }
+
+    /**
+     * Handle HDMI-CEC message coming to the device by invoking the registered
+     * listeners.
+     */
+    public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+        if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) {
+            mIsActiveSource = false;
+        }
+        if (mListeners.size() == 0) {
+            return;
+        }
+        HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+        for (IHdmiCecListener listener : mListeners) {
+            try {
+                listener.onMessageReceived(message);
+            } catch (RemoteException e) {
+                Log.e(TAG, "listener.onMessageReceived failed.");
+            }
+        }
+    }
+
+    public void handleHotplug(boolean connected) {
+        for (IHdmiCecListener listener : mListeners) {
+            try {
+                listener.onCableStatusChanged(connected);
+            } catch (RemoteException e) {
+                Log.e(TAG, "listener.onCableStatusChanged failed.");
+            }
+        }
+    }
+
+    /**
+     * Return the active status of the device.
+     *
+     * @return true if the device is the active source among the connected
+     *         HDMI-CEC-enabled devices; otherwise false.
+     */
+    public boolean isActiveSource() {
+        return mIsActiveSource;
+    }
+
+    /**
+     * Update the active source state of the device.
+     */
+    public void setIsActiveSource(boolean state) {
+        mIsActiveSource = state;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
new file mode 100644
index 0000000..01f2ec3
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java
@@ -0,0 +1,394 @@
+/*
+ * 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 com.android.server.hdmi;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.hardware.hdmi.IHdmiCecService;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Provides a service for sending and processing HDMI-CEC messages, and providing
+ * the information on HDMI settings in general.
+ */
+public final class HdmiCecService extends SystemService {
+    private static final String TAG = "HdmiCecService";
+
+    // Maintains the allocated logical devices. Device type, not logical address,
+    // is used for key as logical address is likely to change over time while
+    // device type is permanent. Type-address mapping is maintained only at
+    // native level.
+    private final SparseArray<HdmiCecDevice> mLogicalDevices = new SparseArray<HdmiCecDevice>();
+
+    // List of IBinder.DeathRecipient instances to handle dead IHdmiCecListener
+    // objects.
+    private final ArrayList<ListenerRecord> mListenerRecords = new ArrayList<ListenerRecord>();
+
+    // Used to synchronize the access to the service.
+    private final Object mLock = new Object();
+
+    // Stores the pointer to the native implementation of the service that
+    // interacts with HAL.
+    private long mNativePtr;
+
+    private static final String PERMISSION = "android.permission.HDMI_CEC";
+
+    // Service name under which it is registered to service manager.
+    // TODO: Move this to Context once HdmiCecManager is introduced.
+    private static final String HDMI_CEC_SERVICE = "hdmi_cec";
+
+    public HdmiCecService(Context context) {
+        super(context);
+    }
+
+    private static native long nativeInit(HdmiCecService service);
+
+    @Override
+    public void onStart() {
+        mNativePtr = nativeInit(this);
+        publishBinderService(HDMI_CEC_SERVICE, new BinderService());
+    }
+
+    /**
+     * Called by native when an HDMI-CEC message arrived. Invokes the registered
+     * listeners to handle the message.
+     */
+    private void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+        // TODO: Messages like <Standby> may not need be passed to listener
+        //       but better be handled in service by turning off the screen
+        //       or putting the device into suspend mode. List up such messages
+        //       and handle them here.
+        int type = HdmiCec.getTypeFromAddress(dstAddress);
+        synchronized (mLock) {
+            if (dstAddress == HdmiCec.ADDR_BROADCAST) {
+                for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                    mLogicalDevices.valueAt(i).handleMessage(srcAddress, dstAddress, opcode,
+                            params);
+                }
+            } else {
+                HdmiCecDevice device = mLogicalDevices.get(type);
+                if (device == null) {
+                    Log.w(TAG, "logical device not found. type: " + type);
+                    return;
+                }
+                device.handleMessage(srcAddress, dstAddress, opcode, params);
+            }
+        }
+    }
+
+    /**
+     * Called by native when internal HDMI hotplug event occurs. Invokes the registered
+     * listeners to handle the event.
+     */
+    private void handleHotplug(boolean connected) {
+        synchronized(mLock) {
+            for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                mLogicalDevices.valueAt(i).handleHotplug(connected);
+            }
+        }
+    }
+
+    /**
+     * Called by native when it needs to know whether we have an active source.
+     * The native part uses the return value to respond to &lt;Request Active
+     * Source &gt;.
+     *
+     * @return type of the device which is active; DEVICE_INACTIVE if there is
+     *        no active logical device in the system.
+     */
+    private int getActiveSource() {
+        synchronized(mLock) {
+            for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                if (mLogicalDevices.valueAt(i).isActiveSource()) {
+                    return mLogicalDevices.keyAt(i);
+                }
+            }
+        }
+        return HdmiCec.DEVICE_INACTIVE;
+    }
+
+    /**
+     * Called by native when a request for the device OSD name was received.
+     * The native part uses the return value to generate the message
+     * &lt;Set Osd Name&gt; in response.
+     */
+    private byte[] getOsdName(int type) {
+        synchronized (mLock) {
+            HdmiCecDevice device = mLogicalDevices.get(type);
+            if (device != null) {
+                return device.getName().getBytes(Charset.forName("US-ASCII"));
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called by native when a request for the menu language of the device was
+     * received. The native part uses the return value to generate the message
+     * &lt;Set Menu Language&gt; in response. The language should be of
+     * the 3-letter format as defined in ISO/FDIS 639-2. We use system default
+     * locale.
+     */
+    private String getLanguage(int type) {
+        return Locale.getDefault().getISO3Language();
+    }
+
+    private void enforceAccessPermission() {
+        getContext().enforceCallingOrSelfPermission(PERMISSION, "HdmiCecService");
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        pw.println("HdmiCecService (dumpsys hdmi_cec)");
+        pw.println("");
+        synchronized (mLock) {
+            for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                HdmiCecDevice device = mLogicalDevices.valueAt(i);
+                pw.println("Device: name=" + device.getName() +
+                           ", type=" + device.getType() +
+                           ", active=" + device.isActiveSource());
+            }
+        }
+    }
+
+    // Remove logical device of a given type.
+    private void removeLogicalDeviceLocked(int type) {
+        ensureValidType(type);
+        mLogicalDevices.remove(type);
+        nativeRemoveLogicalAddress(mNativePtr, type);
+    }
+
+    private static void ensureValidType(int type) {
+        if (!HdmiCec.isValidType(type)) {
+            throw new IllegalArgumentException("invalid type: " + type);
+        }
+    }
+
+    // Return the logical device identified by the given binder token.
+    private HdmiCecDevice getLogicalDeviceLocked(IBinder b) {
+        for (int i = 0; i < mLogicalDevices.size(); ++i) {
+            HdmiCecDevice device = mLogicalDevices.valueAt(i);
+            if (device.getToken() == b) {
+                return device;
+            }
+        }
+        throw new IllegalArgumentException("Device not found");
+    }
+
+    private final class ListenerRecord implements IBinder.DeathRecipient {
+        private final IHdmiCecListener mListener;
+        private final int mType;
+
+        public ListenerRecord(IHdmiCecListener listener, int type) {
+            mListener = listener;
+            mType = type;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mListenerRecords.remove(this);
+                HdmiCecDevice device = mLogicalDevices.get(mType);
+                if (device != null) {
+                    device.removeListener(mListener);
+                    if (!device.hasListener()) {
+                        removeLogicalDeviceLocked(mType);
+                    }
+                }
+            }
+        }
+    }
+
+    private final class BinderService extends IHdmiCecService.Stub {
+
+        @Override
+        public IBinder allocateLogicalDevice(int type, IHdmiCecListener listener) {
+            enforceAccessPermission();
+            ensureValidType(type);
+            if (listener == null) {
+                throw new IllegalArgumentException("listener must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = mLogicalDevices.get(type);
+                if (device != null) {
+                    Log.v(TAG, "Logical address already allocated. Adding listener only.");
+                } else {
+                    int address = nativeAllocateLogicalAddress(mNativePtr, type);
+                    if (!HdmiCec.isValidAddress(address)) {
+                        Log.e(TAG, "Logical address was not allocated");
+                        return null;
+                    } else {
+                        device = new HdmiCecDevice(type);
+                        device.setName(HdmiCec.getDefaultDeviceName(address));
+                        mLogicalDevices.put(type, device);
+                    }
+                }
+
+                // Adds the listener and its monitor
+                ListenerRecord record = new ListenerRecord(listener, type);
+                try {
+                    listener.asBinder().linkToDeath(record, 0);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Listener already died");
+                    if (!device.hasListener()) {
+                         removeLogicalDeviceLocked(type);
+                    }
+                    return null;
+                }
+                mListenerRecords.add(record);
+                device.addListener(listener);
+                return device.getToken();
+            }
+        }
+
+        @Override
+        public void setOsdName(IBinder b, String name) {
+            enforceAccessPermission();
+            if (TextUtils.isEmpty(name)) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                device.setName(name);
+            }
+        }
+
+        @Override
+        public void sendActiveSource(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                device.setIsActiveSource(true);
+                nativeSendActiveSource(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendInactiveSource(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                device.setIsActiveSource(false);
+                nativeSendInactiveSource(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendImageViewOn(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendImageViewOn(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendTextViewOn(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendTextViewOn(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendGiveDevicePowerStatus(IBinder b, int address) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendGiveDevicePowerStatus(mNativePtr, device.getType(), address);
+            }
+        }
+
+        @Override
+        public void removeServiceListener(IBinder b, IHdmiCecListener listener) {
+            enforceAccessPermission();
+            if (listener == null) {
+                throw new IllegalArgumentException("listener must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                for (ListenerRecord record : mListenerRecords) {
+                    if (record.mType == device.getType()
+                            && record.mListener.asBinder() == listener.asBinder()) {
+                        mListenerRecords.remove(record);
+                        device.removeListener(record.mListener);
+                        if (!device.hasListener()) {
+                            removeLogicalDeviceLocked(record.mType);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void sendMessage(IBinder b, HdmiCecMessage message) {
+            enforceAccessPermission();
+            if (message == null) {
+                throw new IllegalArgumentException("message must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendMessage(mNativePtr, device.getType(), message.getDestination(),
+                        message.getOpcode(), message.getParams());
+            }
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission denial: can't dump HdmiCecService from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                dumpInternal(pw);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    private static native int nativeAllocateLogicalAddress(long handler, int deviceType);
+    private static native void nativeRemoveLogicalAddress(long handler, int deviceType);
+    private static native void nativeSendMessage(long handler, int deviceType, int destination,
+            int opcode, byte[] params);
+    private static native void nativeSendActiveSource(long handler, int deviceType);
+    private static native void nativeSendInactiveSource(long handler, int deviceType);
+    private static native void nativeSendImageViewOn(long handler, int deviceType);
+    private static native void nativeSendTextViewOn(long handler, int deviceType);
+    private static native void nativeSendGiveDevicePowerStatus(long handler, int deviceType,
+            int address);
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cb4cf98..a01c586 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -200,6 +200,9 @@
 
     private static final boolean GET_CERTIFICATES = true;
 
+    // Cap the size of permission trees that 3rd party apps can define
+    private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;     // characters of text
+
     private static final int REMOVE_EVENTS =
         FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
     private static final int ADD_EVENTS =
@@ -2357,7 +2360,35 @@
         //if (pi1.descriptionRes != pi2.descriptionRes) return false;
         return true;
     }
-    
+
+    int permissionInfoFootprint(PermissionInfo info) {
+        int size = info.name.length();
+        if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length();
+        if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length();
+        return size;
+    }
+
+    int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+        int size = 0;
+        for (BasePermission perm : mSettings.mPermissions.values()) {
+            if (perm.uid == tree.uid) {
+                size += perm.name.length() + permissionInfoFootprint(perm.perm.info);
+            }
+        }
+        return size;
+    }
+
+    void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+        // We calculate the max size of permissions defined by this uid and throw
+        // if that plus the size of 'info' would exceed our stated maximum.
+        if (tree.uid != Process.SYSTEM_UID) {
+            final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
+            if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) {
+                throw new SecurityException("Permission tree size cap exceeded");
+            }
+        }
+    }
+
     boolean addPermissionLocked(PermissionInfo info, boolean async) {
         if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
             throw new SecurityException("Label must be specified in permission");
@@ -2368,6 +2399,7 @@
         boolean changed = true;
         int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
         if (added) {
+            enforcePermissionCapLocked(info, tree);
             bp = new BasePermission(info.name, tree.sourcePackage,
                     BasePermission.TYPE_DYNAMIC);
         } else if (bp.type != BasePermission.TYPE_DYNAMIC) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 88a27f5..8fed79f 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -24,6 +24,7 @@
 import android.app.ProgressDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetoothManager;
+import android.media.AudioManager;
 import android.nfc.NfcAdapter;
 import android.nfc.INfcAdapter;
 import android.content.BroadcastReceiver;
@@ -498,7 +499,7 @@
             // vibrate before shutting down
             Vibrator vibrator = new SystemVibrator();
             try {
-                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
+                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, AudioManager.STREAM_SYSTEM);
             } catch (Exception e) {
                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                 Log.w(TAG, "Failed to vibrate during shutdown.", e);
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 0607925..1b3887c 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -8,23 +8,26 @@
     $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
     $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
     $(LOCAL_REL_DIR)/onload.cpp
 
+include external/stlport/libstlport.mk
+
 LOCAL_C_INCLUDES += \
     $(JNI_H_INCLUDE) \
     frameworks/base/services \
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 22cc519..da4cc48 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -139,16 +139,21 @@
             }
             endpos++;
         }
-        if (i == 0) {
+        // For now we are not separating out the first irq.
+        // This is because in practice there are always multiple
+        // lines of wakeup reasons, so it is better to just treat
+        // them all together as a single string.
+        if (false && i == 0) {
             firstirq = irq;
         } else {
-            int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq);
+            int len = snprintf(mergedreasonpos, remainreasonlen,
+                    i == 0 ? "%d" : ":%d", irq);
             if (len >= 0 && len < remainreasonlen) {
                 mergedreasonpos += len;
                 remainreasonlen -= len;
             }
         }
-        int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos);
+        int len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
         if (len >= 0 && len < remainreasonlen) {
             mergedreasonpos += len;
             remainreasonlen -= len;
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
new file mode 100644
index 0000000..6170c09
--- /dev/null
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
@@ -0,0 +1,689 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "HdmiCecJni"
+
+#define LOG_NDEBUG 1
+
+#include "ScopedPrimitiveArray.h"
+
+#include <cstring>
+#include <deque>
+#include <map>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+
+static struct {
+    jmethodID handleMessage;
+    jmethodID handleHotplug;
+    jmethodID getActiveSource;
+    jmethodID getOsdName;
+    jmethodID getLanguage;
+} gHdmiCecServiceClassInfo;
+
+#ifndef min
+#define min(a, b) ((a) > (b) ? (b) : (a))
+#endif
+
+class HdmiCecHandler {
+public:
+    enum HdmiCecError {
+        SUCCESS = 0,
+        FAILED = -1
+    };
+
+    // Data type to hold a CEC message or internal event data.
+    typedef union {
+        cec_message_t cec;
+        hotplug_event_t hotplug;
+    } queue_item_t;
+
+    // Entry used for message queue.
+    typedef std::pair<int, const queue_item_t> MessageEntry;
+
+    HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj);
+
+    void initialize();
+
+    // initialize individual logical device.
+    int initLogicalDevice(int type);
+    void releaseLogicalDevice(int type);
+
+    cec_logical_address_t getLogicalAddress(int deviceType);
+    int getDeviceType(cec_logical_address_t addr);
+    void queueMessage(const MessageEntry& message);
+    void queueOutgoingMessage(const cec_message_t& message);
+    void sendReportPhysicalAddress();
+    void sendActiveSource(cec_logical_address_t srcAddr);
+    void sendInactiveSource(cec_logical_address_t srcAddr);
+    void sendImageViewOn(cec_logical_address_t srcAddr);
+    void sendTextViewOn(cec_logical_address_t srcAddr);
+    void sendGiveDevicePowerStatus(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+    void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+            int opcode, int reason);
+    void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+            int version);
+    void sendDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+    void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+    void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+            const char* name, size_t len);
+    void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+
+    void sendCecMessage(const cec_message_t& message);
+
+private:
+    enum {
+        EVENT_TYPE_RX,
+        EVENT_TYPE_TX,
+        EVENT_TYPE_HOTPLUG,
+        EVENT_TYPE_STANDBY
+    };
+
+    static const unsigned int MAX_BUFFER_SIZE = 256;
+    static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+    static const int INACTIVE_DEVICE_TYPE = -1;
+
+    static void onReceived(const hdmi_event_t* event, void* arg);
+    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+
+    void updatePhysicalAddress();
+    void dispatchMessage(const MessageEntry& message);
+    void processIncomingMessage(const cec_message_t& msg);
+
+    // Check the message before we pass it up to framework. If true, we proceed.
+    // otherwise do not propagate it.
+    bool precheckMessage(const cec_message_t& msg);
+
+    // Propagate the message up to Java layer.
+    void propagateMessage(const cec_message_t& msg);
+    void propagateHotplug(bool connected);
+
+    // Handles incoming <Request Active Source> message. If one of logical
+    // devices is active, it should reply with <Active Source> message.
+    void handleRequestActiveSource();
+    void handleGetOsdName(const cec_message_t& msg);
+    void handleGiveDeviceVendorID(const cec_message_t& msg);
+    void handleGetCECVersion(const cec_message_t& msg);
+    void handleGetMenuLanguage(const cec_message_t& msg);
+
+    // Internal thread for message queue handler
+    class HdmiThread : public Thread {
+    public:
+        HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) :
+            Thread(canCallJava),
+            mHdmiCecHandler(hdmiCecHandler) {
+        }
+    private:
+        virtual bool threadLoop() {
+            ALOGV("HdmiThread started");
+            AutoMutex _l(mHdmiCecHandler->mMessageQueueLock);
+            mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock);
+            /* Process all messages in the queue */
+            while (mHdmiCecHandler->mMessageQueue.size() > 0) {
+                MessageEntry entry = mHdmiCecHandler->mMessageQueue.front();
+                mHdmiCecHandler->dispatchMessage(entry);
+            }
+            return true;
+        }
+
+        HdmiCecHandler* mHdmiCecHandler;
+    };
+
+    // device type -> logical address mapping
+    std::map<int, cec_logical_address_t> mLogicalDevices;
+
+    hdmi_cec_device_t* mDevice;
+    jobject mCallbacksObj;
+    Mutex mLock;
+    Mutex mMessageQueueLock;
+    Condition mMessageQueueCondition;
+    sp<HdmiThread> mMessageQueueHandler;
+
+    std::deque<MessageEntry> mMessageQueue;
+    uint16_t mPhysicalAddress;
+};
+
+
+HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) :
+    mDevice(device),
+    mCallbacksObj(callbacksObj) {
+}
+
+void HdmiCecHandler::initialize() {
+    mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this);
+    mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */);
+    mMessageQueueHandler->run("MessageHandler");
+    updatePhysicalAddress();
+}
+
+void HdmiCecHandler::updatePhysicalAddress() {
+    uint16_t addr;
+    if (!mDevice->get_physical_address(mDevice, &addr)) {
+        mPhysicalAddress = addr;
+    } else {
+        mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+    }
+}
+
+int HdmiCecHandler::initLogicalDevice(int type) {
+    cec_logical_address_t addr;
+    int res = mDevice->allocate_logical_address(mDevice, type, &addr);
+
+    if (res != 0) {
+        ALOGE("Logical Address Allocation failed: %d", res);
+    } else {
+        ALOGV("Logical Address Allocation success: %d", addr);
+        mLogicalDevices.insert(std::pair<int, cec_logical_address_t>(type, addr));
+    }
+    return addr;
+}
+
+void HdmiCecHandler::releaseLogicalDevice(int type) {
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(type);
+    if (it != mLogicalDevices.end()) {
+        mLogicalDevices.erase(it);
+    }
+    // TODO: remove the address monitored in HAL as well.
+}
+
+cec_logical_address_t HdmiCecHandler::getLogicalAddress(int mDevicetype) {
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(mDevicetype);
+    if (it != mLogicalDevices.end()) {
+        return it->second;
+    }
+    return CEC_ADDR_UNREGISTERED;
+}
+
+int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+    for (; it != mLogicalDevices.end(); ++it) {
+        if (it->second == addr) {
+            return it->first;
+        }
+    }
+    return INACTIVE_DEVICE_TYPE;
+}
+
+void HdmiCecHandler::queueMessage(const MessageEntry& entry) {
+    AutoMutex _l(mMessageQueueLock);
+    if (mMessageQueue.size() <=  MAX_BUFFER_SIZE) {
+        mMessageQueue.push_back(entry);
+        mMessageQueueCondition.signal();
+    } else {
+        ALOGW("Queue is full! Message dropped.");
+    }
+}
+
+void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) {
+    queue_item_t item;
+    item.cec = message;
+    MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item);
+    queueMessage(entry);
+}
+
+void HdmiCecHandler::sendReportPhysicalAddress() {
+    if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+        ALOGE("Invalid physical address.");
+        return;
+    }
+
+    // Report physical address for each logical one hosted in it.
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+    while (it != mLogicalDevices.end()) {
+        cec_message_t msg;
+        msg.initiator = it->second;  // logical address
+        msg.destination = CEC_ADDR_BROADCAST;
+        msg.length = 4;
+        msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
+        std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+        msg.body[3] = it->first;  // device type
+        queueOutgoingMessage(msg);
+        ++it;
+    }
+}
+
+void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) {
+    if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+        ALOGE("Error getting physical address.");
+        return;
+    }
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_BROADCAST;
+    msg.length = 3;
+    msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE;
+    std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendInactiveSource(cec_logical_address_t srcAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_TV;
+    msg.length = 3;
+    msg.body[0] = CEC_MESSAGE_INACTIVE_SOURCE;
+    if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
+        std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+        queueOutgoingMessage(msg);
+    }
+}
+
+void HdmiCecHandler::sendImageViewOn(cec_logical_address_t srcAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_TV;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_IMAGE_VIEW_ON;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendTextViewOn(cec_logical_address_t srcAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_TV;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_TEXT_VIEW_ON;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendGiveDevicePowerStatus(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr, int opcode, int reason) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 3;
+    msg.body[0] = CEC_MESSAGE_FEATURE_ABORT;
+    msg.body[1] = opcode;
+    msg.body[2] = reason;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr, int version) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 2;
+    msg.body[0] = CEC_MESSAGE_CEC_VERSION;
+    msg.body[1] = version;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendDeviceVendorID(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 4;
+    msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID;
+    uint32_t vendor_id;
+    mDevice->get_vendor_id(mDevice, &vendor_id);
+    std::memcpy(msg.body + 1, &vendor_id, 3);
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+        const char* name, size_t len) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.body[0] = CEC_MESSAGE_SET_OSD_NAME;
+    msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH);
+    std::memcpy(msg.body + 1, name, msg.length - 1);
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    char lang[4];   // buffer for 3-letter language code
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring res = (jstring) env->CallObjectMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.getLanguage,
+            getDeviceType(srcAddr));
+    const char *clang = env->GetStringUTFChars(res, NULL);
+    strlcpy(lang, clang, sizeof(lang));
+    env->ReleaseStringUTFChars(res, clang);
+
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 4;  // opcode (1) + language code (3)
+    msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE;
+    std::memcpy(msg.body + 1, lang, 3);
+    queueOutgoingMessage(msg);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::sendCecMessage(const cec_message_t& message) {
+    AutoMutex _l(mLock);
+    ALOGV("sendCecMessage");
+    mDevice->send_message(mDevice, &message);
+}
+
+// static
+void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) {
+    HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg);
+    if (handler == NULL) {
+        return;
+    }
+    queue_item_t item;
+    if (event->type == HDMI_EVENT_CEC_MESSAGE) {
+        item.cec = event->cec;
+        MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item);
+        handler->queueMessage(entry);
+    } else if (event->type == HDMI_EVENT_HOT_PLUG) {
+        item.hotplug = event->hotplug;
+        MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item);
+        handler->queueMessage(entry);
+    }
+}
+
+// static
+void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        ALOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) {
+    int type = entry.first;
+    mMessageQueueLock.unlock();
+    if (type == EVENT_TYPE_RX) {
+        mMessageQueue.pop_front();
+        processIncomingMessage(entry.second.cec);
+    } else if (type == EVENT_TYPE_TX) {
+        sendCecMessage(entry.second.cec);
+        mMessageQueue.pop_front();
+    } else if (type == EVENT_TYPE_HOTPLUG) {
+        mMessageQueue.pop_front();
+        bool connected = entry.second.hotplug.connected;
+        if (connected) {
+            // TODO: Update logical addresses as well, since they also could have
+            // changed while the cable was disconnected.
+            updatePhysicalAddress();
+        }
+        propagateHotplug(connected);
+    }
+    mMessageQueueLock.lock();
+}
+
+void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) {
+    int opcode = msg.body[0];
+    if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) {
+        sendReportPhysicalAddress();
+    } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) {
+        handleRequestActiveSource();
+    } else if (opcode == CEC_MESSAGE_GET_OSD_NAME) {
+        handleGetOsdName(msg);
+    } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) {
+        handleGiveDeviceVendorID(msg);
+    } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) {
+        handleGetCECVersion(msg);
+    } else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) {
+        handleGetMenuLanguage(msg);
+    } else {
+        if (precheckMessage(msg)) {
+            propagateMessage(msg);
+        }
+    }
+}
+
+bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) {
+    // Check if this is the broadcast message coming to itself, which need not be passed
+    // back to framework. This happens because CEC spec specifies that a physical device
+    // may host multiple logical devices. A broadcast message sent by one of them therefore
+    // should be able to reach the others by the loopback mechanism.
+    //
+    // Currently we don't deal with multiple logical devices, so this is not necessary.
+    // It should be revisited once we support hosting multiple logical devices.
+    int opcode = msg.body[0];
+    if (msg.destination == CEC_ADDR_BROADCAST &&
+            (opcode == CEC_MESSAGE_ACTIVE_SOURCE ||
+             opcode == CEC_MESSAGE_SET_STREAM_PATH ||
+             opcode == CEC_MESSAGE_INACTIVE_SOURCE)) {
+        uint16_t senderAddr;
+        std::memcpy(&senderAddr, &msg.body[1], 2);
+        if (senderAddr == mPhysicalAddress) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void HdmiCecHandler::propagateMessage(const cec_message_t& msg) {
+    int paramLen = msg.length - 1;
+    jint srcAddr = msg.initiator;
+    jint dstAddr = msg.destination;
+    jint opcode = msg.body[0];
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jbyteArray params = env->NewByteArray(paramLen);
+    const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1);
+    if (paramLen > 0) {
+        env->SetByteArrayRegion(params, 0, paramLen, body);
+    }
+    env->CallVoidMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.handleMessage,
+            srcAddr, dstAddr, opcode, params);
+    env->DeleteLocalRef(params);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::propagateHotplug(bool connected) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.handleHotplug,
+            connected);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+
+void HdmiCecHandler::handleRequestActiveSource() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint activeDeviceType = env->CallIntMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.getActiveSource);
+    if (activeDeviceType != INACTIVE_DEVICE_TYPE) {
+        sendActiveSource(getLogicalAddress(activeDeviceType));
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) {
+    cec_logical_address_t addr = msg.destination;
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jbyteArray res = (jbyteArray) env->CallObjectMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.getOsdName,
+            getDeviceType(addr));
+    jbyte *name = env->GetByteArrayElements(res, NULL);
+    if (name != NULL) {
+        sendSetOsdName(addr, msg.initiator, reinterpret_cast<const char *>(name),
+                env->GetArrayLength(res));
+        env->ReleaseByteArrayElements(res, name, JNI_ABORT);
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) {
+    sendDeviceVendorID(msg.destination, msg.initiator);
+}
+
+void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) {
+    int version;
+    mDevice->get_version(mDevice, &version);
+    sendCecVersion(msg.destination, msg.initiator, version);
+}
+
+void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) {
+    sendSetMenuLanguage(msg.destination, msg.initiator);
+}
+
+//------------------------------------------------------------------------------
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
+    int err;
+    hw_module_t* module;
+    err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module));
+    if (err != 0) {
+        ALOGE("Error acquiring hardware module: %d", err);
+        return 0;
+    }
+    hw_device_t* device;
+    err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
+    if (err != 0) {
+        ALOGE("Error opening hardware module: %d", err);
+        return 0;
+    }
+    HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device),
+            env->NewGlobalRef(callbacksObj));
+    handler->initialize();
+
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz,
+            "handleMessage", "(III[B)V");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz,
+            "handleHotplug", "(Z)V");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz,
+            "getActiveSource", "()I");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.getOsdName, clazz,
+            "getOsdName", "(I)[B");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz,
+            "getLanguage", "(I)Ljava/lang/String;");
+
+    return reinterpret_cast<jlong>(handler);
+}
+
+static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType,
+        jint dstAddr, jint opcode, jbyteArray params) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    jsize len = env->GetArrayLength(params);
+    ScopedByteArrayRO paramsPtr(env, params);
+    cec_message_t message;
+    message.initiator = srcAddr;
+    message.destination = static_cast<cec_logical_address_t>(dstAddr);
+    message.length = len + 1;
+    message.body[0] = opcode;
+    std::memcpy(message.body + 1, paramsPtr.get(), len);
+    handler->sendCecMessage(message);
+}
+
+static int nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    return handler->initLogicalDevice(deviceType);
+}
+
+static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
+       jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    return handler->releaseLogicalDevice(deviceType);
+}
+
+static void nativeSendActiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendActiveSource(srcAddr);
+}
+
+static void nativeSendInactiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendInactiveSource(srcAddr);
+}
+
+static void nativeSendImageViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendImageViewOn(srcAddr);
+}
+
+static void nativeSendTextViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendTextViewOn(srcAddr);
+}
+
+static void nativeSendGiveDevicePowerStatus(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType, jint destination) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    cec_logical_address_t dstAddr = static_cast<cec_logical_address_t>(destination);
+    handler->sendGiveDevicePowerStatus(srcAddr, dstAddr);
+}
+
+static JNINativeMethod sMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J",
+            (void *)nativeInit },
+    { "nativeSendMessage", "(JIII[B)V",
+            (void *)nativeSendMessage },
+    { "nativeAllocateLogicalAddress", "(JI)I",
+            (void *)nativeAllocateLogicalAddress },
+    { "nativeRemoveLogicalAddress", "(JI)V",
+            (void *)nativeRemoveLogicalAddress },
+    { "nativeSendActiveSource", "(JI)V",
+            (void *)nativeSendActiveSource },
+    { "nativeSendInactiveSource", "(JI)V",
+            (void *)nativeSendInactiveSource },
+    { "nativeSendImageViewOn", "(JI)V",
+            (void *)nativeSendImageViewOn },
+    { "nativeSendTextViewOn", "(JI)V",
+            (void *)nativeSendTextViewOn },
+    { "nativeSendGiveDevicePowerStatus", "(JII)V",
+            (void *)nativeSendGiveDevicePowerStatus }
+};
+
+#define CLASS_PATH "com/android/server/hdmi/HdmiCecService"
+
+int register_android_server_hdmi_HdmiCecService(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+    return 0;
+}
+
+}  /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ac6585b..bf9f7f4 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -21,6 +21,7 @@
 
 namespace android {
 int register_android_server_AlarmManagerService(JNIEnv* env);
+int register_android_server_AssetAtlasService(JNIEnv* env);
 int register_android_server_BatteryStatsService(JNIEnv* env);
 int register_android_server_ConsumerIrService(JNIEnv *env);
 int register_android_server_InputApplicationHandle(JNIEnv* env);
@@ -29,15 +30,15 @@
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
 int register_android_server_SerialService(JNIEnv* env);
+int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_UsbDeviceManager(JNIEnv* env);
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
 int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
-int register_android_server_AssetAtlasService(JNIEnv* env);
 int register_android_server_dreams_McuHal(JNIEnv* env);
+int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
 };
 
 using namespace android;
@@ -71,6 +72,7 @@
     register_android_server_ConsumerIrService(env);
     register_android_server_dreams_McuHal(env);
     register_android_server_BatteryStatsService(env);
+    register_android_server_hdmi_HdmiCecService(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b4c099d..c925669 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -118,6 +118,8 @@
             "com.android.server.wifi.WifiService";
     private static final String WIFI_P2P_SERVICE_CLASS =
             "com.android.server.wifi.p2p.WifiP2pService";
+    private static final String HDMI_CEC_SERVICE_CLASS =
+            "com.android.server.hdmi.HdmiCecService";
 
     private final int mFactoryTestMode;
     private Timer mProfilerSnapshotTimer;
@@ -889,6 +891,12 @@
                 reportWtf("starting MediaSessionService", e);
             }
 
+            try {
+                mSystemServiceManager.startService(HDMI_CEC_SERVICE_CLASS);
+            } catch (Throwable e) {
+                reportWtf("starting HdmiCec Service", e);
+            }
+
             if (!disableNonCoreServices) {
                 try {
                     Slog.i(TAG, "Media Router Service");
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 37176d6..b0296a0 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -18,12 +18,13 @@
 
 import android.accounts.Account;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Settings;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.server.content.SyncOperation;
-
 /**
  * You can run those tests with:
  *
@@ -35,6 +36,21 @@
 
 public class SyncOperationTest extends AndroidTestCase {
 
+    Account mDummy;
+    /** Indicate an unimportant long that we're not testing. */
+    long mUnimportantLong = 0L;
+    /** Empty bundle. */
+    Bundle mEmpty;
+    /** Silly authority. */
+    String mAuthority;
+
+    @Override
+    public void setUp() {
+        mDummy = new Account("account1", "type1");
+        mEmpty = new Bundle();
+        mAuthority = "authority1";
+    }
+
     @SmallTest
     public void testToKey() {
         Account account1 = new Account("account1", "type1");
@@ -111,35 +127,64 @@
 
     @SmallTest
     public void testCompareTo() {
-        Account dummy = new Account("account1", "type1");
-        Bundle b1 = new Bundle();
-        final long unimportant = 0L;
         long soon = 1000;
         long soonFlex = 50;
         long after = 1500;
         long afterFlex = 100;
-        SyncOperation op1 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
-                "authority1", b1, soon, soonFlex, unimportant, unimportant, true);
+        SyncOperation op1 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+                "authority1", mEmpty, soon, soonFlex, mUnimportantLong, mUnimportantLong, true);
 
         // Interval disjoint from and after op1.
-        SyncOperation op2 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
-                "authority1", b1, after, afterFlex, unimportant, unimportant, true);
+        SyncOperation op2 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+                "authority1", mEmpty, after, afterFlex, mUnimportantLong, mUnimportantLong, true);
 
         // Interval equivalent to op1, but expedited.
         Bundle b2 = new Bundle();
         b2.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
-        SyncOperation op3 = new SyncOperation(dummy, 0, 0, 0,
-                "authority1", b2, soon, soonFlex, unimportant, unimportant, true);
+        SyncOperation op3 = new SyncOperation(mDummy, 0, 0, 0,
+                "authority1", b2, -1, soonFlex, mUnimportantLong, mUnimportantLong, true);
 
         // Interval overlaps but not equivalent to op1.
-        SyncOperation op4 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
-                "authority1", b1, soon + 100, soonFlex + 100, unimportant, unimportant, true);
+        SyncOperation op4 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+                "authority1", mEmpty, soon + 100, soonFlex + 100, mUnimportantLong, mUnimportantLong, true);
 
         assertTrue(op1.compareTo(op2) == -1);
         assertTrue("less than not transitive.", op2.compareTo(op1) == 1);
-        assertTrue(op1.compareTo(op3) == 1);
+        assertTrue("Expedited sync not smaller than non-expedited.", op1.compareTo(op3) == 1);
         assertTrue("greater than not transitive. ", op3.compareTo(op1) == -1);
-        assertTrue("overlapping intervals not the same.", op1.compareTo(op4) == 0);
-        assertTrue("equality not transitive.", op4.compareTo(op1) == 0);
+        assertTrue("overlapping intervals not correctly compared.", op1.compareTo(op4) == -1);
+        assertTrue("equality not transitive.", op4.compareTo(op1) == 1);
+    }
+
+    @SmallTest
+    public void testCopyConstructor() {
+        long fiveSecondsFromNow = 5 * 1000L;
+        long twoSecondsFlex = 2 * 1000L;
+        long eightSeconds = 8 * 1000L;
+        long fourSeconds = 4 * 1000L;
+
+        Bundle withExpedited = new Bundle();
+        withExpedited.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        SyncOperation op = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_USER_START,
+                mAuthority, withExpedited, fiveSecondsFromNow, twoSecondsFlex,
+                eightSeconds /* backoff */, fourSeconds /* delayUntil */, true);
+        // Create another sync op to be rerun in 5 minutes.
+        long now = SystemClock.elapsedRealtime();
+        SyncOperation copy = new SyncOperation(op, fiveSecondsFromNow * 60);
+        // Copying an expedited sync to be re-run should not keep expedited property.
+        assertFalse("A rescheduled sync based off an expedited should not be expedited!",
+                copy.isExpedited());
+        assertFalse("A rescheduled sync based off an expedited should not have expedited=true in"
+                + "its bundle.",
+                copy.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false));
+        assertTrue("Copied sync is not respecting new provided run-time.",
+                copy.latestRunTime == (now + fiveSecondsFromNow * 60));
+        assertTrue("A rescheduled sync should not have any flex.",
+                copy.flexTime == 0L);
+        assertTrue("A rescheduled op should honour the old op's backoff.",
+                copy.backoff == eightSeconds);
+        assertTrue("A rescheduled op should honour the old op's delayUntil param.",
+                copy.delayUntil == fourSeconds);
+
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index 70fd810..ae1967e 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -131,7 +131,7 @@
         assertEquals(sop.target.userId, popRetrieved.target.userId);
         assertEquals(sop.reason, popRetrieved.reason);
         assertEquals(sop.syncSource, popRetrieved.syncSource);
-        assertEquals(sop.expedited, popRetrieved.expedited);
+        assertEquals(sop.isExpedited(), popRetrieved.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
     }
 
@@ -169,7 +169,7 @@
         assertEquals(deleted.target.userId, popDeleted.target.userId);
         assertEquals(deleted.reason, popDeleted.reason);
         assertEquals(deleted.syncSource, popDeleted.syncSource);
-        assertEquals(deleted.expedited, popDeleted.expedited);
+        assertEquals(deleted.isExpedited(), popDeleted.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(deleted.extras, popDeleted.extras));
         // Delete one to force write-all
         engine.deleteFromPending(popDeleted);
@@ -192,7 +192,7 @@
         assertEquals(sop.target.userId, popRetrieved.target.userId);
         assertEquals(sop.reason, popRetrieved.reason);
         assertEquals(sop.syncSource, popRetrieved.syncSource);
-        assertEquals(sop.expedited, popRetrieved.expedited);
+        assertEquals(sop.isExpedited(), popRetrieved.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
 
         popRetrieved = pops.get(1);
@@ -202,7 +202,7 @@
         assertEquals(sop1.target.userId, popRetrieved.target.userId);
         assertEquals(sop1.reason, popRetrieved.reason);
         assertEquals(sop1.syncSource, popRetrieved.syncSource);
-        assertEquals(sop1.expedited, popRetrieved.expedited);
+        assertEquals(sop1.isExpedited(), popRetrieved.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(sop1.extras, popRetrieved.extras));
     }
 
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index aff0088..652998e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1317,9 +1317,9 @@
                         curIsFormatted = false;
                         // Untranslatable strings must only exist in the default [empty] locale
                         if (locale.size() > 0) {
-                            fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
-                                    " in locale '%s'\n", String8(name).string(),
-                                    bundle->getResourceSourceDirs()[0],
+                            SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
+                                    "string '%s' marked untranslatable but exists in locale '%s'\n",
+                                    String8(name).string(),
                                     locale.string());
                             // hasErrors = localHasErrors = true;
                         } else {
@@ -1330,7 +1330,10 @@
                             // having no default translation.
                         }
                     } else {
-                        outTable->addLocalization(name, locale);
+                        outTable->addLocalization(
+                                name,
+                                locale,
+                                SourcePos(in->getPrintableSource(), block.getLineNumber()));
                     }
 
                     if (formatted == false16) {
@@ -2570,9 +2573,9 @@
 
 
 void
-ResourceTable::addLocalization(const String16& name, const String8& locale)
+ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
 {
-    mLocalizations[name].insert(locale);
+    mLocalizations[name][locale] = src;
 }
 
 
@@ -2592,21 +2595,22 @@
     const String8 defaultLocale;
 
     // For all strings...
-    for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+    for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin();
          nameIter != mLocalizations.end();
          nameIter++) {
-        const set<String8>& configSet = nameIter->second;   // naming convenience
+        const map<String8, SourcePos>& configSrcMap = nameIter->second;
 
         // Look for strings with no default localization
-        if (configSet.count(defaultLocale) == 0) {
-            fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
-                    String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
-            for (set<String8>::const_iterator locales = configSet.begin();
-                 locales != configSet.end();
-                 locales++) {
-                fprintf(stdout, " %s", (*locales).string());
+        if (configSrcMap.count(defaultLocale) == 0) {
+            SourcePos().warning("string '%s' has no default translation.",
+                    String8(nameIter->first).string());
+            if (mBundle->getVerbose()) {
+                for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin();
+                    locales != configSrcMap.end();
+                    locales++) {
+                    locales->second.printf("locale %s found", locales->first.string());
+                }
             }
-            fprintf(stdout, "\n");
             // !!! TODO: throw an error here in some circumstances
         }
 
@@ -2616,6 +2620,8 @@
             const char* start = allConfigs;
             const char* comma;
             
+            set<String8> missingConfigs;
+            AaptLocaleValue locale;
             do {
                 String8 config;
                 comma = strchr(start, ',');
@@ -2626,27 +2632,38 @@
                     config.setTo(start);
                 }
 
+                if (!locale.initFromFilterString(config)) {
+                    continue;
+                }
+
                 // don't bother with the pseudolocale "zz_ZZ"
                 if (config != "zz_ZZ") {
-                    if (configSet.find(config) == configSet.end()) {
+                    if (configSrcMap.find(config) == configSrcMap.end()) {
                         // okay, no specific localization found.  it's possible that we are
                         // requiring a specific regional localization [e.g. de_DE] but there is an
                         // available string in the generic language localization [e.g. de];
                         // consider that string to have fulfilled the localization requirement.
                         String8 region(config.string(), 2);
-                        if (configSet.find(region) == configSet.end()) {
-                            if (configSet.count(defaultLocale) == 0) {
-                                fprintf(stdout, "aapt: warning: "
-                                        "**** string '%s' has no default or required localization "
-                                        "for '%s' in %s\n",
-                                        String8(nameIter->first).string(),
-                                        config.string(),
-                                        mBundle->getResourceSourceDirs()[0]);
-                            }
+                        if (configSrcMap.find(region) == configSrcMap.end() &&
+                                configSrcMap.count(defaultLocale) == 0) {
+                            missingConfigs.insert(config);
                         }
                     }
                 }
-           } while (comma != NULL);
+            } while (comma != NULL);
+
+            if (!missingConfigs.empty()) {
+                String8 configStr;
+                for (set<String8>::iterator iter = missingConfigs.begin();
+                     iter != missingConfigs.end();
+                     iter++) {
+                    configStr.appendFormat(" %s", iter->string());
+                }
+                SourcePos().warning("string '%s' is missing %u required localizations:%s",
+                        String8(nameIter->first).string(),
+                        (unsigned int)missingConfigs.size(),
+                        configStr.string());
+            }
         }
     }
 
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index a3e0666..75005cd 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -220,7 +220,7 @@
 
     status_t assignResourceIds();
     status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
-    void addLocalization(const String16& name, const String8& locale);
+    void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
     status_t validateLocalizations(void);
 
     status_t flatten(Bundle*, const sp<AaptFile>& dest);
@@ -551,7 +551,7 @@
     Bundle* mBundle;
     
     // key = string resource name, value = set of locales in which that name is defined
-    map<String16, set<String8> > mLocalizations;
+    map<String16, map<String8, SourcePos> > mLocalizations;
 };
 
 #endif
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index e2a921c..ae25047 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -10,17 +10,20 @@
 // =============================================================================
 struct ErrorPos
 {
+    enum Level {
+        NOTE,
+        WARNING,
+        ERROR
+    };
+
     String8 file;
     int line;
     String8 error;
-    bool fatal;
+    Level level;
 
     ErrorPos();
     ErrorPos(const ErrorPos& that);
-    ErrorPos(const String8& file, int line, const String8& error, bool fatal);
-    ~ErrorPos();
-    bool operator<(const ErrorPos& rhs) const;
-    bool operator==(const ErrorPos& rhs) const;
+    ErrorPos(const String8& file, int line, const String8& error, Level level);
     ErrorPos& operator=(const ErrorPos& rhs);
 
     void print(FILE* to) const;
@@ -29,7 +32,7 @@
 static vector<ErrorPos> g_errors;
 
 ErrorPos::ErrorPos()
-    :line(-1), fatal(false)
+    :line(-1), level(NOTE)
 {
 }
 
@@ -37,61 +40,52 @@
     :file(that.file),
      line(that.line),
      error(that.error),
-     fatal(that.fatal)
+     level(that.level)
 {
 }
 
-ErrorPos::ErrorPos(const String8& f, int l, const String8& e, bool fat)
+ErrorPos::ErrorPos(const String8& f, int l, const String8& e, Level lev)
     :file(f),
      line(l),
      error(e),
-     fatal(fat)
+     level(lev)
 {
 }
 
-ErrorPos::~ErrorPos()
-{
-}
-
-bool
-ErrorPos::operator<(const ErrorPos& rhs) const
-{
-    if (this->file < rhs.file) return true;
-    if (this->file == rhs.file) {
-        if (this->line < rhs.line) return true;
-        if (this->line == rhs.line) {
-            if (this->error < rhs.error) return true;
-        }
-    }
-    return false;
-}
-
-bool
-ErrorPos::operator==(const ErrorPos& rhs) const
-{
-    return this->file == rhs.file
-            && this->line == rhs.line
-            && this->error == rhs.error;
-}
-
 ErrorPos&
 ErrorPos::operator=(const ErrorPos& rhs)
 {
     this->file = rhs.file;
     this->line = rhs.line;
     this->error = rhs.error;
+    this->level = rhs.level;
     return *this;
 }
 
 void
 ErrorPos::print(FILE* to) const
 {
-    const char* type = fatal ? "error:" : "warning:";
+    const char* type = "";
+    switch (level) {
+    case NOTE:
+        type = "note: ";
+        break;
+    case WARNING:
+        type = "warning: ";
+        break;
+    case ERROR:
+        type = "error: ";
+        break;
+    }
     
-    if (this->line >= 0) {
-        fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string());
+    if (!this->file.isEmpty()) {
+        if (this->line >= 0) {
+            fprintf(to, "%s:%d: %s%s\n", this->file.string(), this->line, type, this->error.string());
+        } else {
+            fprintf(to, "%s: %s%s\n", this->file.string(), type, this->error.string());
+        }
     } else {
-        fprintf(to, "%s: %s %s\n", this->file.string(), type, this->error.string());
+        fprintf(to, "%s%s\n", type, this->error.string());
     }
 }
 
@@ -116,40 +110,34 @@
 {
 }
 
-int
+void
 SourcePos::error(const char* fmt, ...) const
 {
-    int retval=0;
-    char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    String8 msg = String8::formatV(fmt, ap);
     va_end(ap);
-    char* p = buf + retval - 1;
-    while (p > buf && *p == '\n') {
-        *p = '\0';
-        p--;
-    }
-    g_errors.push_back(ErrorPos(this->file, this->line, String8(buf), true));
-    return retval;
+    g_errors.push_back(ErrorPos(this->file, this->line, msg, ErrorPos::ERROR));
 }
 
-int
+void
 SourcePos::warning(const char* fmt, ...) const
 {
-    int retval=0;
-    char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    String8 msg = String8::formatV(fmt, ap);
     va_end(ap);
-    char* p = buf + retval - 1;
-    while (p > buf && *p == '\n') {
-        *p = '\0';
-        p--;
-    }
-    ErrorPos(this->file, this->line, String8(buf), false).print(stderr);
-    return retval;
+    ErrorPos(this->file, this->line, msg, ErrorPos::WARNING).print(stderr);
+}
+
+void
+SourcePos::printf(const char* fmt, ...) const
+{
+    va_list ap;
+    va_start(ap, fmt);
+    String8 msg = String8::formatV(fmt, ap);
+    va_end(ap);
+    ErrorPos(this->file, this->line, msg, ErrorPos::NOTE).print(stderr);
 }
 
 bool
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
index 33f72a9..4ce817f 100644
--- a/tools/aapt/SourcePos.h
+++ b/tools/aapt/SourcePos.h
@@ -17,8 +17,9 @@
     SourcePos();
     ~SourcePos();
 
-    int error(const char* fmt, ...) const;
-    int warning(const char* fmt, ...) const;
+    void error(const char* fmt, ...) const;
+    void warning(const char* fmt, ...) const;
+    void printf(const char* fmt, ...) const;
 
     static bool hasErrors();
     static void printErrors(FILE* to);