Merge "Adding shell commands for grant/revoke of bind app widget permisison." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index f0ee69d..a49e0f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4588,6 +4588,7 @@
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
@@ -5354,6 +5355,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public boolean getScreenCaptureDisabled(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public boolean hasAnyCaCertsInstalled();
@@ -5398,6 +5400,7 @@
     method public void setProfileName(android.content.ComponentName, java.lang.String);
     method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
+    method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
@@ -15017,6 +15020,7 @@
     field public static final java.lang.String KEY_REFERENCE_CLOCK_ID = "reference-clock-id";
     field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
     field public static final java.lang.String KEY_SAMPLE_RATE = "sample-rate";
+    field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
     field public static final java.lang.String KEY_WIDTH = "width";
     field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
     field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -16592,7 +16596,7 @@
   }
 
   public static final class MediaController.VolumeInfo {
-    method public int getAudioStream();
+    method public android.media.AudioAttributes getAudioAttributes();
     method public int getCurrentVolume();
     method public int getMaxVolume();
     method public int getVolumeControl();
@@ -16617,7 +16621,7 @@
     method public void setMediaRouter(android.media.routing.MediaRouter);
     method public void setMetadata(android.media.MediaMetadata);
     method public void setPlaybackState(android.media.session.PlaybackState);
-    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToLocal(android.media.AudioAttributes);
     method public void setPlaybackToRemote(android.media.VolumeProvider);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
@@ -27276,6 +27280,7 @@
     ctor public StatusBarNotification(android.os.Parcel);
     method public android.service.notification.StatusBarNotification clone();
     method public int describeContents();
+    method public java.lang.String getGroupKey();
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
@@ -28689,15 +28694,15 @@
   }
 
   public class PhoneAccount implements android.os.Parcelable {
-    ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
+    ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.CharSequence, java.lang.CharSequence, boolean);
     method public int describeContents();
     method public android.telecomm.PhoneAccountHandle getAccountHandle();
     method public int getCapabilities();
     method public android.net.Uri getHandle();
     method public android.graphics.drawable.Drawable getIcon(android.content.Context);
     method public int getIconResId();
-    method public java.lang.String getLabel();
-    method public java.lang.String getShortDescription();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.CharSequence getShortDescription();
     method public java.lang.String getSubscriptionNumber();
     method public boolean isVideoCallingSupported();
     method public void writeToParcel(android.os.Parcel, int);
@@ -28791,13 +28796,13 @@
   }
 
   public final class StatusHints implements android.os.Parcelable {
-    ctor public StatusHints(android.content.ComponentName, java.lang.String, int, android.os.Bundle);
+    ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
     method public int describeContents();
     method public android.content.ComponentName getComponentName();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Drawable getIcon(android.content.Context);
     method public int getIconId();
-    method public java.lang.String getLabel();
+    method public java.lang.CharSequence getLabel();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index da59a03..5782edc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -799,6 +799,12 @@
     public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
 
     /**
+     * {@link #extras} key: the indices of actions to be shown in the compact view,
+     * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
+     */
+    public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+
+    /**
      * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
      * displayed in the heads up space.
      *
@@ -3362,6 +3368,9 @@
             if (mToken != null) {
                 extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
             }
+            if (mActionsToShowInCompact != null) {
+                extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
+            }
         }
 
         private RemoteViews generateMediaActionButton(Action action) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index efeded5..a193a34 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1775,6 +1775,46 @@
     }
 
     /**
+     * Called by a device/profile owner to set whether the screen capture is disabled.
+     *
+     * <p>The calling device admin must be a device or profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     */
+    public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Determine whether or not screen capture has been disabled by the current
+     * admin, if specified, or all admins.
+     * @param admin The name of the admin component to check, or null to check if any admins
+     * have disabled screen capture.
+     */
+    public boolean getScreenCaptureDisabled(ComponentName admin) {
+        return getScreenCaptureDisabled(admin, UserHandle.myUserId());
+    }
+
+    /** @hide per-user version */
+    public boolean getScreenCaptureDisabled(ComponentName admin, int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.getScreenCaptureDisabled(admin, userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by an application that is administering the device to disable keyguard customizations,
      * such as widgets. After setting this, keyguard features will be disabled according to the
      * provided feature list.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5fc8c5f..6499ae4 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -88,6 +88,9 @@
     void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
     boolean getCameraDisabled(in ComponentName who, int userHandle);
 
+    void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+    boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
+
     void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
     int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b7344b7..95c62e0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2837,6 +2837,7 @@
      * @see android.net.NetworkScoreManager
      * @hide
      */
+    @SystemApi
     public static final String NETWORK_SCORE_SERVICE = "network_score";
 
     /**
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 38ddede..5face69 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1824,15 +1824,7 @@
             remainder = languageTag.substring(separator);
         }
 
-        if ("id".equals(language)) {
-            return "in" + remainder;
-        } else if ("yi".equals(language)) {
-            return "ji" + remainder;
-        } else if ("he".equals(language)) {
-            return "iw" + remainder;
-        } else {
-            return languageTag;
-        }
+        return Locale.adjustLanguageCode(language) + remainder;
     }
 
     /**
diff --git a/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
new file mode 100644
index 0000000..b51fa2b
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
@@ -0,0 +1,415 @@
+/*
+ * 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.annotation.SystemApi;
+import android.hardware.hdmi.HdmiRecordSources.AnalogueServiceSource;
+import android.hardware.hdmi.HdmiRecordSources.DigitalServiceSource;
+import android.hardware.hdmi.HdmiRecordSources.ExternalPhysicalAddress;
+import android.hardware.hdmi.HdmiRecordSources.ExternalPlugData;
+import android.hardware.hdmi.HdmiRecordSources.RecordSource;
+import android.util.Log;
+
+/**
+ * Container for timer record source used for timer recording. Timer source consists of two parts,
+ * timer info and record source.
+ * <p>
+ * Timer info contains all timing information used for recording. It consists of the following
+ * values.
+ * <ul>
+ * <li>[Day of Month]
+ * <li>[Month of Year]
+ * <li>[Start Time]
+ * <li>[Duration]
+ * <li>[Recording Sequence]
+ * </ul>
+ * <p>
+ * Record source containers all program information used for recording.
+ * For more details, look at {@link HdmiRecordSources}.
+ * <p>
+ * Usage
+ * <pre>
+ * TimeOrDuration startTime = HdmiTimerRecordSources.ofTime(18, 00);  // 6PM.
+ * TimeOrDuration duration = HdmiTimerRecordSource.ofDuration(1, 00);  // 1 hour duration.
+ * // For 1 hour from 6PM, August 10th every SaturDay and Sunday.
+ * TimerInfo timerInfo = HdmiTimerRecordSource.timerInfoOf(10, 8, starTime, duration,
+ *            HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SATURDAY |
+ *            HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SUNDAY);
+ * // create digital source.
+ * DigitalServiceSource recordSource = HdmiRecordSource.ofDvb(...);
+ * TimerRecordSource source = ofDigitalSource(timerInfo, recordSource);
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public class HdmiTimerRecordSources {
+    private static final String TAG = "HdmiTimerRecordingSources";
+
+    private HdmiTimerRecordSources() {}
+
+    /**
+     * Create {@link TimerRecordSource} for digital source which is used for &lt;Set Digital
+     * Timer&gt;.
+     *
+     * @param timerInfo timer info used for timer recording
+     * @param source digital source used for timer recording
+     * @return {@link TimerRecordSource}
+     * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+     */
+    public static TimerRecordSource ofDigitalSource(TimerInfo timerInfo,
+            DigitalServiceSource source) {
+        checkTimerRecordSourceInputs(timerInfo, source);
+        return new TimerRecordSource(timerInfo, source);
+    }
+
+    /**
+     * Create {@link TimerRecordSource} for analogue source which is used for &lt;Set Analogue
+     * Timer&gt;.
+     *
+     * @param timerInfo timer info used for timer recording
+     * @param source digital source used for timer recording
+     * @return {@link TimerRecordSource}
+     * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+     */
+    public static TimerRecordSource ofAnalogueSource(TimerInfo timerInfo,
+            AnalogueServiceSource source) {
+        checkTimerRecordSourceInputs(timerInfo, source);
+        return new TimerRecordSource(timerInfo, source);
+    }
+
+    /**
+     * Create {@link TimerRecordSource} for external plug which is used for &lt;Set External
+     * Timer&gt;.
+     *
+     * @param timerInfo timer info used for timer recording
+     * @param source digital source used for timer recording
+     * @return {@link TimerRecordSource}
+     * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+     */
+    public static TimerRecordSource ofExternalPlug(TimerInfo timerInfo, ExternalPlugData source) {
+        checkTimerRecordSourceInputs(timerInfo, source);
+        return new TimerRecordSource(timerInfo,
+                new ExternalSourceDecorator(source, EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG));
+    }
+
+    /**
+     * Create {@link TimerRecordSource} for external physical address which is used for &lt;Set
+     * External Timer&gt;.
+     *
+     * @param timerInfo timer info used for timer recording
+     * @param source digital source used for timer recording
+     * @return {@link TimerRecordSource}
+     * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+     */
+    public static TimerRecordSource ofExternalPhysicalAddress(TimerInfo timerInfo,
+            ExternalPhysicalAddress source) {
+        checkTimerRecordSourceInputs(timerInfo, source);
+        return new TimerRecordSource(timerInfo,
+                new ExternalSourceDecorator(source,
+                        EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS));
+    }
+
+    private static void checkTimerRecordSourceInputs(TimerInfo timerInfo, RecordSource source) {
+        if (timerInfo == null) {
+            Log.w(TAG, "TimerInfo should not be null.");
+            throw new IllegalArgumentException("TimerInfo should not be null.");
+        }
+        if (source == null) {
+            Log.w(TAG, "source should not be null.");
+            throw new IllegalArgumentException("source should not be null.");
+        }
+    }
+
+    /**
+     * Create {@link Duration} for time value.
+     *
+     * @param hour hour in range of [0, 24]
+     * @param minute minute in range of [0, 60]
+     * @return {@link Duration}
+     * @throws IllegalArgumentException if hour or minute is out of range
+     */
+    public static Time ofTime(int hour, int minute) {
+        checkTimeValue(hour, minute);
+        return new Time(hour, minute);
+    }
+
+    private static void checkTimeValue(int hour, int minute) {
+        if (hour < 0 || hour > 24) {
+            throw new IllegalArgumentException("Hour should be in rage of [0, 24]:" + hour);
+        }
+        if (minute < 0 || minute > 60) {
+            throw new IllegalArgumentException("Minute should be in rage of [0, 60]:" + minute);
+        }
+    }
+
+    /**
+     * Create {@link Duration} for duration value.
+     *
+     * @param hour hour in range of [0, 90]
+     * @param minute minute in range of [0, 60]
+     * @return {@link Duration}
+     * @throws IllegalArgumentException if hour or minute is out of range
+     */
+    public static Duration ofDuration(int hour, int minute) {
+        checkDurationValue(hour, minute);
+        return new Duration(hour, minute);
+    }
+
+    private static void checkDurationValue(int hour, int minute) {
+        if (hour < 0 || hour > 99) {
+            throw new IllegalArgumentException("Hour should be in rage of [0, 99]:" + hour);
+        }
+        if (minute < 0 || minute > 60) {
+            throw new IllegalArgumentException("minute should be in rage of [0, 60]:" + minute);
+        }
+    }
+
+    private static class TimeUnit {
+        protected final int mHour;
+        protected final int mMinute;
+
+        protected TimeUnit(int hour, int minute) {
+            mHour = hour;
+            mMinute = minute;
+        }
+
+        protected int toByteArray(byte[] data, int index) {
+            data[index] = toBcdByte(mHour);
+            data[index + 1] = toBcdByte(mMinute);
+            return 2;
+        }
+
+        protected static byte toBcdByte(int value) {
+            int digitOfTen = (value / 10) % 10;
+            int digitOfOne = value % 10;
+            return (byte) ((digitOfTen << 4) | digitOfOne);
+        }
+    }
+
+    /**
+     * Place holder for time value.
+     */
+    public static class Time extends TimeUnit {
+        private Time(int hour, int minute) {
+            super(hour, minute);
+        }
+    }
+
+    /**
+     * Place holder for duration value.
+     */
+    public static class Duration extends TimeUnit {
+        private Duration(int hour, int minute) {
+            super(hour, minute);
+        }
+    }
+
+    /**
+     * Fields for recording sequence.
+     * The following can be merged by OR(|) operation.
+     */
+    public static final int RECORDING_SEQUENCE_REPEAT_ONCE_ONLY = 0;
+    public static final int RECORDING_SEQUENCE_REPEAT_SUNDAY = 1 << 0;
+    public static final int RECORDING_SEQUENCE_REPEAT_MONDAY = 1 << 1;
+    public static final int RECORDING_SEQUENCE_REPEAT_TUESDAY = 1 << 2;
+    public static final int RECORDING_SEQUENCE_REPEAT_WEDNESDAY = 1 << 3;
+    public static final int RECORDING_SEQUENCE_REPEAT_THURSDAY = 1 << 4;
+    public static final int RECORDING_SEQUENCE_REPEAT_FRIDAY = 1 << 5;
+    public static final int RECORDING_SEQUENCE_REPEAT_SATUREDAY = 1 << 6;
+
+    private static final int RECORDING_SEQUENCE_REPEAT_MASK =
+            (RECORDING_SEQUENCE_REPEAT_SUNDAY | RECORDING_SEQUENCE_REPEAT_MONDAY |
+            RECORDING_SEQUENCE_REPEAT_TUESDAY | RECORDING_SEQUENCE_REPEAT_WEDNESDAY |
+            RECORDING_SEQUENCE_REPEAT_THURSDAY | RECORDING_SEQUENCE_REPEAT_FRIDAY |
+            RECORDING_SEQUENCE_REPEAT_SATUREDAY);
+
+    /**
+     * Create {@link TimerInfo} with the given information.
+     *
+     * @param dayOfMonth day of month
+     * @param monthOfYear month of year
+     * @param startTime start time in {@link Time}
+     * @param duration duration in {@link Duration}
+     * @param recordingSequence recording sequence. Use RECORDING_SEQUENCE_REPEAT_ONCE_ONLY for no
+     *            repeat. Otherwise use combination of {@link #RECORDING_SEQUENCE_REPEAT_SUNDAY},
+     *            {@link #RECORDING_SEQUENCE_REPEAT_MONDAY},
+     *            {@link #RECORDING_SEQUENCE_REPEAT_TUESDAY},
+     *            {@link #RECORDING_SEQUENCE_REPEAT_WEDNESDAY},
+     *            {@link #RECORDING_SEQUENCE_REPEAT_THURSDAY},
+     *            {@link #RECORDING_SEQUENCE_REPEAT_FRIDAY},
+     *            {@link #RECORDING_SEQUENCE_REPEAT_SATUREDAY}.
+     * @return {@link TimerInfo}.
+     * @throws IllegalArgumentException if input value is invalid
+     */
+    public static TimerInfo timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime,
+            Duration duration, int recordingSequence) {
+        if (dayOfMonth < 0 || dayOfMonth > 31) {
+            throw new IllegalArgumentException(
+                    "Day of month should be in range of [0, 31]:" + dayOfMonth);
+        }
+        if (monthOfYear < 1 || monthOfYear > 12) {
+            throw new IllegalArgumentException(
+                    "Month of year should be in range of [1, 12]:" + monthOfYear);
+        }
+        checkTimeValue(startTime.mHour, startTime.mMinute);
+        checkDurationValue(duration.mHour, duration.mMinute);
+        // Recording sequence should use least 7 bits or no bits.
+        if ((recordingSequence != 0)
+                && ((recordingSequence & ~RECORDING_SEQUENCE_REPEAT_MASK) != 0)) {
+            throw new IllegalArgumentException(
+                    "Invalid reecording sequence value:" + recordingSequence);
+        }
+
+        return new TimerInfo(dayOfMonth, monthOfYear, startTime, duration, recordingSequence);
+    }
+
+    /**
+     * Container basic timer information. It consists of the following fields.
+     * <ul>
+     * <li>[Day of Month]
+     * <li>[Month of Year]
+     * <li>[Start Time]
+     * <li>[Duration]
+     * <li>[Recording Sequence]
+     * </ul>
+     */
+    public static class TimerInfo {
+        private static final int DAY_OF_MONTH_SIZE = 1;
+        private static final int MONTH_OF_YEAR_SIZE = 1;
+        private static final int START_TIME_SIZE = 2; // 1byte for hour and 1byte for minute.
+        private static final int DURATION_SIZE = 2; // 1byte for hour and 1byte for minute.
+        private static final int RECORDING_SEQUENCE_SIZE = 1;
+        private static final int BASIC_INFO_SIZE = DAY_OF_MONTH_SIZE + MONTH_OF_YEAR_SIZE
+                + START_TIME_SIZE + DURATION_SIZE + RECORDING_SEQUENCE_SIZE;
+
+        /** Day of month. */
+        private final int mDayOfMonth;
+        /** Month of year. */
+        private final int mMonthOfYear;
+        /**
+         * Time of day.
+         * [Hour][Minute]. 0 &lt;= Hour &lt;= 24, 0 &lt;= Minute &lt;= 60 in BCD format.
+         */
+        private final Time mStartTime;
+        /**
+         * Duration. [Hour][Minute].
+         * 0 &lt;= Hour &lt;= 99, 0 &lt;= Minute &lt;= 60 in BCD format.
+         * */
+        private final Duration mDuration;
+        /**
+         * Indicates if recording is repeated and, if so, on which days. For repeated recordings,
+         * the recording sequence value is the bitwise OR of the days when recordings are required.
+         * [Recording Sequence] shall be set to 0x00 when the recording is not repeated. Bit 7 is
+         * reserved and shall be set to zero.
+         */
+        private final int mRecordingSequence;
+
+        private TimerInfo(int dayOfMonth, int monthOfYear, Time startTime,
+                Duration duration, int recordingSequence) {
+            mDayOfMonth = dayOfMonth;
+            mMonthOfYear = monthOfYear;
+            mStartTime = startTime;
+            mDuration = duration;
+            mRecordingSequence = recordingSequence;
+        }
+
+        int toByteArray(byte[] data, int index) {
+            // [Day of Month]
+            data[index] = (byte) mDayOfMonth;
+            index += DAY_OF_MONTH_SIZE;
+            // [Month of Year]
+            data[index] = (byte) mMonthOfYear;
+            index += MONTH_OF_YEAR_SIZE;
+            // [Start Time]
+            index += mStartTime.toByteArray(data, index);
+            index += mDuration.toByteArray(data, index);
+            // [Duration]
+            // [Recording Sequence]
+            data[index] = (byte) mRecordingSequence;
+            return getDataSize();
+        }
+
+        int getDataSize() {
+            return BASIC_INFO_SIZE;
+        }
+    }
+
+    /**
+     * Record source container for timer record. This is used to set parameter for &lt;Set Digital
+     * Timer&gt;, &lt;Set Analogue Timer&gt;, and &lt;Set External Timer&gt; message.
+     * <p>
+     * In order to create this from each source type, use one of helper method.
+     * <ul>
+     * <li>{@link #ofDigitalSource} for digital source
+     * <li>{@link #ofAnalogueSource} for analogue source
+     * <li>{@link #ofExternalPlug} for external plug type
+     * <li>{@link #ofExternalPhysicalAddress} for external physical address type.
+     * </ul>
+     */
+    public static class TimerRecordSource {
+        private final RecordSource mRecordSource;
+        private final TimerInfo mTimerInfo;
+
+        private TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource) {
+            mTimerInfo = timerInfo;
+            mRecordSource = recordSource;
+        }
+
+        int getDataSize() {
+            return mTimerInfo.getDataSize() + mRecordSource.getDataSize(false);
+        }
+
+        int toByteArray(byte[] data, int index) {
+            // Basic infos including [Day of Month] [Month of Year] [Start Time] [Duration]
+            // [Recording Sequence]
+            index += mTimerInfo.toByteArray(data, index);
+            // [Record Source]
+            mRecordSource.toByteArray(false, data, index);
+            return getDataSize();
+        }
+    }
+
+    /**
+     * External source specifier types.
+     */
+    private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG = 4;
+    private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS = 5;
+
+    /**
+     * Decorator for external source. Beside digital or analogue source, external source starts with
+     * [External Source Specifier] because it covers both external plug type and external specifier.
+     */
+    private static class ExternalSourceDecorator extends RecordSource {
+        private final RecordSource mRecordSource;
+        private final int mExternalSourceSpecifier;
+
+        private ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier) {
+            // External source has one byte field for [External Source Specifier].
+            super(recordSource.mSourceType, recordSource.getDataSize(false) + 1);
+            mRecordSource = recordSource;
+            mExternalSourceSpecifier = externalSourceSpecifier;
+        }
+
+        @Override
+        int extraParamToByteArray(byte[] data, int index) {
+            data[index] = (byte) mExternalSourceSpecifier;
+            mRecordSource.toByteArray(false, data, index + 1);
+            return getDataSize(false);
+        }
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 6d64a1d..c02ff8a 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -168,29 +168,56 @@
     /**
      * Set {@link RecordRequestListener} to hdmi control service.
      */
-    public void setRecordRequestListener(RecordRequestListener listener) {
+    public void setOneTouchRecordRequestListener(RecordRequestListener listener) {
         try {
-            mService.setRecordRequestListener(getCallbackWrapper(listener));
+            mService.setOneTouchRecordRequestListener(getCallbackWrapper(listener));
         } catch (RemoteException e) {
             Log.e(TAG, "failed to set record request listener: ", e);
         }
     }
 
     /**
-     * Start recording with the given recorder address and recorder source.
-     * <p>Usage
+     * Start one touch recording with the given recorder address and recorder source.
+     * <p>
+     * Usage
      * <pre>
      * HdmiTvClient tvClient = ....;
      * // for own source.
      * OwnSource ownSource = ownHdmiRecordSources.ownSource();
-     * tvClient.startRecord(recorderAddress, ownSource);
+     * tvClient.startOneTouchRecord(recorderAddress, ownSource);
      * </pre>
      */
-    public void startRecord(int recorderAddress, HdmiRecordSources.RecordSource source) {
+    public void startOneTouchRecord(int recorderAddress, HdmiRecordSources.RecordSource source) {
         try {
             byte[] data = new byte[source.getDataSize(true)];
             source.toByteArray(true, data, 0);
-            mService.startRecord(recorderAddress, data);
+            mService.startOneTouchRecord(recorderAddress, data);
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to start record: ", e);
+        }
+    }
+
+    /**
+     * Start timer recording with the given recoder address and recorder source.
+     * <p>
+     * Usage
+     * <pre>
+     * HdmiTvClient tvClient = ....;
+     * // create timer info
+     * TimerInfo timerInfo = HdmiTimerRecourdSources.timerInfoOf(...);
+     * // for digital source.
+     * DigitalServiceSource recordSource = HdmiRecordSources.ofDigitalService(...);
+     * // create timer recording source.
+     * TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
+     * tvClient.startTimerRecording(recorderAddress, source);
+     * </pre>
+     */
+    public void startTimerRecording(int recorderAddress,
+            HdmiTimerRecordSources.TimerRecordSource source) {
+        try {
+            byte[] data = new byte[source.getDataSize()];
+            source.toByteArray(data, 0);
+            mService.startTimerRecording(recorderAddress, data);
         } catch (RemoteException e) {
             Log.e(TAG, "failed to start record: ", e);
         }
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 6f3763b..53b8b3f 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -55,13 +55,14 @@
     void setArcMode(boolean enabled);
     void setOption(int option, int value);
     void setProhibitMode(boolean enabled);
-    oneway void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
-    oneway void setSystemAudioMute(boolean mute);
+    void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
+    void setSystemAudioMute(boolean mute);
     void setInputChangeListener(IHdmiInputChangeListener listener);
     List<HdmiCecDeviceInfo> getInputDevices();
     void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
             boolean hasVendorId);
     void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
-    void setRecordRequestListener(IHdmiRecordRequestListener listener);
-    void startRecord(int recorderAddress, in byte[] recordSource);
+    void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener);
+    void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
+    void startTimerRecording(int recorderAddress, in byte[] recordSource);
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index aab7b1f..545b19a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -346,10 +346,6 @@
         public abstract void noteWifiBatchedScanStoppedLocked(long elapsedRealtime);
         public abstract void noteWifiMulticastEnabledLocked(long elapsedRealtime);
         public abstract void noteWifiMulticastDisabledLocked(long elapsedRealtime);
-        public abstract void noteAudioTurnedOnLocked(long elapsedRealtime);
-        public abstract void noteAudioTurnedOffLocked(long elapsedRealtime);
-        public abstract void noteVideoTurnedOnLocked(long elapsedRealtime);
-        public abstract void noteVideoTurnedOffLocked(long elapsedRealtime);
         public abstract void noteActivityResumedLocked(long elapsedRealtime);
         public abstract void noteActivityPausedLocked(long elapsedRealtime);
         public abstract long getWifiRunningTime(long elapsedRealtimeUs, int which);
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index e7cdc4e..e5a5292 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -30,6 +30,7 @@
     private final int id;
     private final String tag;
     private final String key;
+    private final String groupKey;
 
     private final int uid;
     private final String opPkg;
@@ -65,6 +66,7 @@
         this.notification.setUser(user);
         this.postTime = postTime;
         this.key = key();
+        this.groupKey = groupKey();
     }
 
     public StatusBarNotification(Parcel in) {
@@ -84,12 +86,26 @@
         this.notification.setUser(this.user);
         this.postTime = in.readLong();
         this.key = key();
+        this.groupKey = groupKey();
     }
 
     private String key() {
         return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
     }
 
+    private String groupKey() {
+        final String group = getNotification().getGroup();
+        final String sortKey = getNotification().getSortKey();
+        if (group == null && sortKey == null) {
+            // a group of one
+            return key;
+        }
+        return user.getIdentifier() + "|" + pkg + "|" +
+                (group == null
+                        ? "p:" + notification.priority
+                        : "g:" + group);
+    }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(this.pkg);
         out.writeString(this.opPkg);
@@ -240,4 +256,11 @@
     public String getKey() {
         return key;
     }
+
+    /**
+     * A key that indicates the group with which this message ranks.
+     */
+    public String getGroupKey() {
+        return groupKey;
+    }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a61d771..5157c41 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -149,6 +149,12 @@
     // boolean string as parsed by SystemProperties.getBoolean().
     void setStrictModeVisualIndicatorPreference(String enabled);
 
+    /**
+     * Update the windowmanagers cached value of
+     * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+     */
+    void updateScreenCaptureDisabled(int userId);
+
     // These can only be called with the SET_ORIENTATION permission.
     /**
      * Update the current screen rotation based on the current state of
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d91eb69..5655506 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -31,6 +31,8 @@
     void noteStopVideo(int uid);
     void noteStartAudio(int uid);
     void noteStopAudio(int uid);
+    void noteResetVideo();
+    void noteResetAudio();
 
     // Remaining methods are only used in Java.
     byte[] getStatistics();
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index a866ca7..229df8f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -36,7 +36,6 @@
 import android.widget.TextView;
 
 import java.text.Collator;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -106,22 +105,21 @@
         return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */);
     }
 
-    public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
-            final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+    public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
         final Resources resources = context.getResources();
 
-        String[] locales = Resources.getSystem().getAssets().getLocales();
+        final String[] locales = Resources.getSystem().getAssets().getLocales();
         List<String> localeList = new ArrayList<String>(locales.length);
         Collections.addAll(localeList, locales);
         if (isInDeveloperMode) {
             if (!localeList.contains("zz_ZZ")) {
                 localeList.add("zz_ZZ");
             }
-        /** - TODO: Enable when zz_ZY Pseudolocale is complete
-         *  if (!localeList.contains("zz_ZY")) {
-         *      localeList.add("zz_ZY");
-         *  }
-         */
+            /** - TODO: Enable when zz_ZY Pseudolocale is complete
+             *  if (!localeList.contains("zz_ZY")) {
+             *      localeList.add("zz_ZY");
+             *	}
+             */
         }
 
         Collections.sort(localeList);
@@ -179,6 +177,13 @@
         }
 
         Collections.sort(localeInfos);
+        return localeInfos;
+    }
+
+    public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+            final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+        final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
+
         final LayoutInflater inflater =
                 (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dec94f2..aad1156 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -172,6 +172,8 @@
     final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
     final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
             new SparseArray<ArrayList<StopwatchTimer>>();
+    final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<StopwatchTimer>();
 
     // Last partial timers we use for distributing CPU usage.
     final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
@@ -256,10 +258,10 @@
     boolean mPhoneOn;
     StopwatchTimer mPhoneOnTimer;
 
-    boolean mAudioOn;
+    int mAudioOnNesting;
     StopwatchTimer mAudioOnTimer;
 
-    boolean mVideoOn;
+    int mVideoOnNesting;
     StopwatchTimer mVideoOnTimer;
 
     boolean mFlashlightOn;
@@ -1472,6 +1474,13 @@
             }
         }
 
+        void stopAllRunningLocked(long elapsedRealtimeMs) {
+            if (mNesting > 0) {
+                mNesting = 1;
+                stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
         // Update the total time for all other running Timers with the same type as this Timer
         // due to a change in timer count
         private static long refreshTimersLocked(long batteryRealtime,
@@ -3187,27 +3196,29 @@
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (!mAudioOn) {
+        if (mAudioOnNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            mAudioOn = true;
             mAudioOnTimer.startRunningLocked(elapsedRealtime);
         }
+        mAudioOnNesting++;
         getUidStatsLocked(uid).noteAudioTurnedOnLocked(elapsedRealtime);
     }
 
     public void noteAudioOffLocked(int uid) {
+        if (mAudioOnNesting == 0) {
+            return;
+        }
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (mAudioOn) {
+        if (--mAudioOnNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            mAudioOn = false;
             mAudioOnTimer.stopRunningLocked(elapsedRealtime);
         }
         getUidStatsLocked(uid).noteAudioTurnedOffLocked(elapsedRealtime);
@@ -3217,32 +3228,68 @@
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (!mVideoOn) {
+        if (mVideoOnNesting == 0) {
             mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            mVideoOn = true;
             mVideoOnTimer.startRunningLocked(elapsedRealtime);
         }
+        mVideoOnNesting++;
         getUidStatsLocked(uid).noteVideoTurnedOnLocked(elapsedRealtime);
     }
 
     public void noteVideoOffLocked(int uid) {
+        if (mVideoOnNesting == 0) {
+            return;
+        }
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (mVideoOn) {
+        if (--mVideoOnNesting == 0) {
             mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            mVideoOn = false;
             mVideoOnTimer.stopRunningLocked(elapsedRealtime);
         }
         getUidStatsLocked(uid).noteVideoTurnedOffLocked(elapsedRealtime);
     }
 
+    public void noteResetAudioLocked() {
+        if (mAudioOnNesting > 0) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mAudioOnNesting = 0;
+            mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mAudioOnTimer.stopAllRunningLocked(elapsedRealtime);
+            for (int i=0; i<mUidStats.size(); i++) {
+                BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+                uid.noteResetAudioLocked(elapsedRealtime);
+            }
+        }
+    }
+
+    public void noteResetVideoLocked() {
+        if (mVideoOnNesting > 0) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mAudioOnNesting = 0;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mVideoOnTimer.stopAllRunningLocked(elapsedRealtime);
+            for (int i=0; i<mUidStats.size(); i++) {
+                BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+                uid.noteResetVideoLocked(elapsedRealtime);
+            }
+        }
+    }
+
     public void noteActivityResumedLocked(int uid) {
         uid = mapUid(uid);
         getUidStatsLocked(uid).noteActivityResumedLocked(SystemClock.elapsedRealtime());
@@ -3855,10 +3902,7 @@
         boolean mWifiMulticastEnabled;
         StopwatchTimer mWifiMulticastTimer;
 
-        boolean mAudioTurnedOn;
         StopwatchTimer mAudioTurnedOnTimer;
-
-        boolean mVideoTurnedOn;
         StopwatchTimer mVideoTurnedOnTimer;
 
         StopwatchTimer mForegroundActivityTimer;
@@ -4073,52 +4117,48 @@
         public StopwatchTimer createAudioTurnedOnTimerLocked() {
             if (mAudioTurnedOnTimer == null) {
                 mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
-                        null, mOnBatteryTimeBase);
+                        mAudioTurnedOnTimers, mOnBatteryTimeBase);
             }
             return mAudioTurnedOnTimer;
         }
 
-        @Override
         public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
-            if (!mAudioTurnedOn) {
-                mAudioTurnedOn = true;
-                createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+            createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+        }
+
+        public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
+            if (mAudioTurnedOnTimer != null) {
+                mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
             }
         }
 
-        @Override
-        public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
-            if (mAudioTurnedOn) {
-                mAudioTurnedOn = false;
-                if (mAudioTurnedOnTimer != null) {
-                    mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                }
+        public void noteResetAudioLocked(long elapsedRealtimeMs) {
+            if (mAudioTurnedOnTimer != null) {
+                mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
             }
         }
 
         public StopwatchTimer createVideoTurnedOnTimerLocked() {
             if (mVideoTurnedOnTimer == null) {
                 mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
-                        null, mOnBatteryTimeBase);
+                        mVideoTurnedOnTimers, mOnBatteryTimeBase);
             }
             return mVideoTurnedOnTimer;
         }
 
-        @Override
         public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
-            if (!mVideoTurnedOn) {
-                mVideoTurnedOn = true;
-                createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+            createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+        }
+
+        public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
+            if (mVideoTurnedOnTimer != null) {
+                mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
             }
         }
 
-        @Override
-        public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
-            if (mVideoTurnedOn) {
-                mVideoTurnedOn = false;
-                if (mVideoTurnedOnTimer != null) {
-                    mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                }
+        public void noteResetVideoLocked(long elapsedRealtimeMs) {
+            if (mVideoTurnedOnTimer != null) {
+                mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
             }
         }
 
@@ -4416,11 +4456,9 @@
             }
             if (mAudioTurnedOnTimer != null) {
                 active |= !mAudioTurnedOnTimer.reset(false);
-                active |= mAudioTurnedOn;
             }
             if (mVideoTurnedOnTimer != null) {
                 active |= !mVideoTurnedOnTimer.reset(false);
-                active |= mVideoTurnedOn;
             }
             if (mForegroundActivityTimer != null) {
                 active |= !mForegroundActivityTimer.reset(false);
@@ -4803,17 +4841,15 @@
             } else {
                 mWifiMulticastTimer = null;
             }
-            mAudioTurnedOn = false;
             if (in.readInt() != 0) {
                 mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
-                        null, mOnBatteryTimeBase, in);
+                        mAudioTurnedOnTimers, mOnBatteryTimeBase, in);
             } else {
                 mAudioTurnedOnTimer = null;
             }
-            mVideoTurnedOn = false;
             if (in.readInt() != 0) {
                 mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
-                        null, mOnBatteryTimeBase, in);
+                        mVideoTurnedOnTimers, mOnBatteryTimeBase, in);
             } else {
                 mVideoTurnedOnTimer = null;
             }
@@ -7554,11 +7590,9 @@
             if (in.readInt() != 0) {
                 u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
             }
-            u.mAudioTurnedOn = false;
             if (in.readInt() != 0) {
                 u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
             }
-            u.mVideoTurnedOn = false;
             if (in.readInt() != 0) {
                 u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
             }
@@ -8071,9 +8105,9 @@
             mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i,
                     null, mOnBatteryTimeBase, in);
         }
-        mAudioOn = false;
+        mAudioOnNesting = 0;
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
-        mVideoOn = false;
+        mVideoOnNesting = 0;
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
         mFlashlightOn = false;
         mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
@@ -8125,6 +8159,8 @@
         mWifiScanTimers.clear();
         mWifiBatchedScanTimers.clear();
         mWifiMulticastTimers.clear();
+        mAudioTurnedOnTimers.clear();
+        mVideoTurnedOnTimers.clear();
 
         sNumSpeedSteps = in.readInt();
 
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
deleted file mode 100644
index 9005188..0000000
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.net;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-public class NetworkKeyTest extends TestCase {
-    public void testValidWifiKey_utf8() {
-        new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03");
-        new WifiKey("\"\"", "AB:CD:01:EF:23:03");
-    }
-
-    public void testValidWifiKey_hex() {
-        new WifiKey("0x1234abcd", "AB:CD:01:EF:23:03");
-    }
-
-    public void testInvalidWifiKey_empty() {
-        try {
-            new WifiKey("", "AB:CD:01:EF:23:03");
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected - empty SSID
-        }
-    }
-
-    public void testInvalidWifiKey_unquotedUtf8() {
-        try {
-            new WifiKey("unquotedSsid", "AB:CD:01:EF:23:03");
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected - empty SSID
-        }
-    }
-
-    public void testInvalidWifiKey_invalidHex() {
-        try {
-            new WifiKey("0x\"nothex\"", "AB:CD:01:EF:23:03");
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected - empty SSID
-        }
-    }
-
-    public void testInvalidWifiKey_shortBssid() {
-        try {
-            new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23");
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected - BSSID too short
-        }
-    }
-
-    public void testInvalidWifiKey_longBssid() {
-        try {
-            new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03:11");
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected - BSSID too long
-        }
-    }
-
-    public void testParceling() {
-        WifiKey wifiKey = new WifiKey("\"ssid\"", "00:00:00:00:00:00");
-        NetworkKey networkKey = new NetworkKey(wifiKey);
-        Parcel parcel = null;
-        try {
-            parcel = Parcel.obtain();
-            parcel.writeParcelable(networkKey, 0);
-            parcel.setDataPosition(0);
-            networkKey = parcel.readParcelable(getClass().getClassLoader());
-        } finally {
-            if (parcel != null) {
-                parcel.recycle();
-            }
-        }
-
-        assertEquals(NetworkKey.TYPE_WIFI, networkKey.type);
-        assertEquals("\"ssid\"", networkKey.wifiKey.ssid);
-        assertEquals("00:00:00:00:00:00", networkKey.wifiKey.bssid);
-    }
-}
diff --git a/core/tests/coretests/src/android/net/RssiCurveTest.java b/core/tests/coretests/src/android/net/RssiCurveTest.java
deleted file mode 100644
index d4438df..0000000
--- a/core/tests/coretests/src/android/net/RssiCurveTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.net;
-
-import junit.framework.TestCase;
-
-public class RssiCurveTest extends TestCase {
-    public void testLookupScore_constantCurve() {
-        RssiCurve curve = new RssiCurve(-100, 200, new byte[] { 10 });
-        assertEquals(10, curve.lookupScore(-200));
-        assertEquals(10, curve.lookupScore(-100));
-        assertEquals(10, curve.lookupScore(0));
-        assertEquals(10, curve.lookupScore(100));
-        assertEquals(10, curve.lookupScore(200));
-    }
-
-    public void testLookupScore_changingCurve() {
-        RssiCurve curve = new RssiCurve(-100, 100, new byte[] { -10, 10 });
-        assertEquals(-10, curve.lookupScore(-200));
-        assertEquals(-10, curve.lookupScore(-100));
-        assertEquals(-10, curve.lookupScore(-50));
-        assertEquals(10, curve.lookupScore(0));
-        assertEquals(10, curve.lookupScore(50));
-        assertEquals(10, curve.lookupScore(100));
-        assertEquals(10, curve.lookupScore(200));
-    }
-}
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
deleted file mode 100644
index 7ab69ad..0000000
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.net;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-
-public class ScoredNetworkTest extends TestCase {
-    private static final RssiCurve CURVE =
-            new RssiCurve(-110, 10, new byte[] {0, 1, 2, 3, 4, 5, 6, 7});
-
-    public void testInvalidCurve_nullBuckets() {
-        try {
-            new RssiCurve(-110, 10, null);
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-    }
-
-    public void testInvalidCurve_emptyBuckets() {
-        try {
-            new RssiCurve(-110, 10, new byte[] {});
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-    }
-
-    public void testParceling() {
-        NetworkKey key = new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00"));
-        ScoredNetwork network = new ScoredNetwork(key, CURVE);
-        Parcel parcel = null;
-        try {
-            parcel = Parcel.obtain();
-            parcel.writeParcelable(network, 0);
-            parcel.setDataPosition(0);
-            network = parcel.readParcelable(getClass().getClassLoader());
-        } finally {
-            if (parcel != null) {
-                parcel.recycle();
-            }
-        }
-        assertEquals(CURVE.start, network.rssiCurve.start);
-        assertEquals(CURVE.bucketWidth, network.rssiCurve.bucketWidth);
-        assertTrue(Arrays.equals(CURVE.rssiBuckets, network.rssiCurve.rssiBuckets));
-    }
-}
diff --git a/media/java/android/media/AudioAttributes.aidl b/media/java/android/media/AudioAttributes.aidl
new file mode 100644
index 0000000..04587f9
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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.media;
+
+parcelable AudioAttributes;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b5cebe3..a95348f 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -213,6 +213,18 @@
     public static final String KEY_I_FRAME_INTERVAL = "i-frame-interval";
 
     /**
+     * A key describing the temporal layering schema.  This is an optional parameter
+     * that applies only to video encoders.  Use {@link MediaCodec#getInputFormat}
+     * after {@link MediaCodec#configure configure} to query if the encoder supports
+     * the desired schema. Supported values are {@code webrtc.vp8.1-layer},
+     * {@code webrtc.vp8.2-layer}, {@code webrtc.vp8.3-layer}, and {@code none}.
+     * If the encoder does not support temporal layering, the input format will
+     * not have an entry with this key.
+     * The associated value is a string.
+     */
+    public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
+
+    /**
      * @hide
      */
     public static final String KEY_STRIDE = "stride";
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 7d1de24..7cda961 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -2220,7 +2220,9 @@
                 }
             } else {
                 // We only know how to handle local and remote, fall back to local if not remote.
-                session.setPlaybackToLocal(mPlaybackStream);
+                AudioAttributes.Builder bob = new AudioAttributes.Builder();
+                bob.setLegacyStreamType(mPlaybackStream);
+                session.setPlaybackToLocal(bob.build());
                 mSvp = null;
             }
         }
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index a92350b..d87a69e 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,6 +16,7 @@
 package android.media.session;
 
 import android.content.ComponentName;
+import android.media.AudioAttributes;
 import android.media.MediaMetadata;
 import android.media.routing.IMediaRouter;
 import android.media.session.ISessionController;
@@ -42,6 +43,7 @@
     void setRatingType(int type);
 
     // These commands relate to volume handling
-    void configureVolumeHandling(int type, int arg1, int arg2);
+    void setPlaybackToLocal(in AudioAttributes attributes);
+    void setPlaybackToRemote(int control, int max);
     void setCurrentVolume(int currentVolume);
 }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 7fedd82..171f4c9 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
@@ -206,7 +207,7 @@
     public @Nullable VolumeInfo getVolumeInfo() {
         try {
             ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
-            return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+            return new VolumeInfo(result.volumeType, result.audioAttrs, result.controlType,
                     result.maxVolume, result.currentVolume);
 
         } catch (RemoteException e) {
@@ -216,8 +217,8 @@
     }
 
     /**
-     * Set the volume of the stream or output this session is playing on. The
-     * command will be ignored if it does not support
+     * Set the volume of the output this session is playing on. The command will
+     * be ignored if it does not support
      * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
      * {@link AudioManager} may be used to affect the handling.
      *
@@ -234,8 +235,8 @@
     }
 
     /**
-     * Adjust the volume of the stream or output this session is playing on. The
-     * direction must be one of {@link AudioManager#ADJUST_LOWER},
+     * Adjust the volume of the output this session is playing on. The direction
+     * must be one of {@link AudioManager#ADJUST_LOWER},
      * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
      * The command will be ignored if the session does not support
      * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
@@ -570,17 +571,17 @@
      */
     public static final class VolumeInfo {
         private final int mVolumeType;
-        private final int mAudioStream;
         private final int mVolumeControl;
         private final int mMaxVolume;
         private final int mCurrentVolume;
+        private final AudioAttributes mAudioAttrs;
 
         /**
          * @hide
          */
-        public VolumeInfo(int type, int stream, int control, int max, int current) {
+        public VolumeInfo(int type, AudioAttributes attrs, int control, int max, int current) {
             mVolumeType = type;
-            mAudioStream = stream;
+            mAudioAttrs = attrs;
             mVolumeControl = control;
             mMaxVolume = max;
             mCurrentVolume = current;
@@ -600,14 +601,15 @@
         }
 
         /**
-         * Get the stream this is currently controlling volume on. When the volume
-         * type is {@link MediaSession#PLAYBACK_TYPE_REMOTE} this value does not
-         * have meaning and should be ignored.
+         * Get the audio attributes for this session. The attributes will affect
+         * volume handling for the session. When the volume type is
+         * {@link MediaSession#PLAYBACK_TYPE_REMOTE} these may be ignored by the
+         * remote volume handler.
          *
-         * @return The stream this session is playing on.
+         * @return The attributes for this session.
          */
-        public int getAudioStream() {
-            return mAudioStream;
+        public AudioAttributes getAudioAttributes() {
+            return mAudioAttrs;
         }
 
         /**
@@ -679,7 +681,7 @@
         public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
             MediaController controller = mController.get();
             if (controller != null) {
-                VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioStream, pvi.controlType,
+                VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
                         pvi.maxVolume, pvi.currentVolume);
                 controller.postMessage(MSG_UPDATE_VOLUME, info, null);
             }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 086cd23..6662303 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
@@ -268,18 +269,22 @@
     }
 
     /**
-     * Set the stream this session is playing on. This will affect the system's
-     * volume handling for this session. If {@link #setPlaybackToRemote} was
-     * previously called it will stop receiving volume commands and the system
-     * will begin sending volume changes to the appropriate stream.
+     * Set the attributes for this session's audio. This will affect the
+     * system's volume handling for this session. If
+     * {@link #setPlaybackToRemote} was previously called it will stop receiving
+     * volume commands and the system will begin sending volume changes to the
+     * appropriate stream.
      * <p>
-     * By default sessions are on {@link AudioManager#STREAM_MUSIC}.
+     * By default sessions use attributes for media.
      *
-     * @param stream The {@link AudioManager} stream this session is playing on.
+     * @param attributes The {@link AudioAttributes} for this session's audio.
      */
-    public void setPlaybackToLocal(int stream) {
+    public void setPlaybackToLocal(AudioAttributes attributes) {
+        if (attributes == null) {
+            throw new IllegalArgumentException("Attributes cannot be null for local playback.");
+        }
         try {
-            mBinder.configureVolumeHandling(PLAYBACK_TYPE_LOCAL, stream, 0);
+            mBinder.setPlaybackToLocal(attributes);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
         }
@@ -288,9 +293,10 @@
     /**
      * Configure this session to use remote volume handling. This must be called
      * to receive volume button events, otherwise the system will adjust the
-     * current stream volume for this session. If {@link #setPlaybackToLocal}
-     * was previously called that stream will stop receiving volume changes for
-     * this session.
+     * appropriate stream volume for this session. If
+     * {@link #setPlaybackToLocal} was previously called the system will stop
+     * handling volume changes for this session and pass them to the volume
+     * provider instead.
      *
      * @param volumeProvider The provider that will handle volume changes. May
      *            not be null.
@@ -308,7 +314,7 @@
         });
 
         try {
-            mBinder.configureVolumeHandling(PLAYBACK_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+            mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
                     volumeProvider.getMaxVolume());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
index 166ccd3..e71b539 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.java
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -15,6 +15,7 @@
 
 package android.media.session;
 
+import android.media.AudioAttributes;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,15 +27,16 @@
  */
 public class ParcelableVolumeInfo implements Parcelable {
     public int volumeType;
-    public int audioStream;
+    public AudioAttributes audioAttrs;
     public int controlType;
     public int maxVolume;
     public int currentVolume;
 
-    public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+    public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
+            int maxVolume,
             int currentVolume) {
         this.volumeType = volumeType;
-        this.audioStream = audioStream;
+        this.audioAttrs = audioAttrs;
         this.controlType = controlType;
         this.maxVolume = maxVolume;
         this.currentVolume = currentVolume;
@@ -42,10 +44,10 @@
 
     public ParcelableVolumeInfo(Parcel from) {
         volumeType = from.readInt();
-        audioStream = from.readInt();
         controlType = from.readInt();
         maxVolume = from.readInt();
         currentVolume = from.readInt();
+        audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
     }
 
     @Override
@@ -56,10 +58,10 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(volumeType);
-        dest.writeInt(audioStream);
         dest.writeInt(controlType);
         dest.writeInt(maxVolume);
         dest.writeInt(currentVolume);
+        audioAttrs.writeToParcel(dest, flags);
     }
 
 
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index e98c5c8..d2071ef 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -120,6 +121,15 @@
     }
 
     /**
+     * Returns true, if {@code channelUri} is a channel URI for a passthrough TV input.
+     * @hide
+     */
+    @SystemApi
+    public static final boolean isChannelUriForPassthroughTvInput(Uri channelUri) {
+        return channelUri.toString().endsWith(PATH_PASSTHROUGH);
+    }
+
+    /**
      * Builds a URI that points to a channel logo. See {@link Channels.Logo}.
      *
      * @param channelId The ID of the channel whose logo is pointed to.
@@ -1099,6 +1109,7 @@
              * @return {@code true} if the genre is canonical, otherwise {@code false}.
              * @hide
              */
+            @SystemApi
             public static boolean isCanonical(String genre) {
                 return CANONICAL_GENRES.contains(genre);
             }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 331283e..52c0534 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -28,6 +29,7 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -40,6 +42,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.InputStream;
 
 /**
  * This class is used to specify meta information of a TV input.
@@ -107,6 +110,8 @@
     private String mSetupActivity;
     private String mSettingsActivity;
     private int mType = TYPE_TUNER;
+    private String mLabel;
+    private Uri mIconUri;
 
     static {
         sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
@@ -134,7 +139,7 @@
             throws XmlPullParserException, IOException {
         return createTvInputInfo(context, service, generateInputIdForComponentName(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)),
-                null, TYPE_TUNER);
+                null, TYPE_TUNER, null, null);
     }
 
     /**
@@ -143,13 +148,21 @@
      *
      * @param service The ResolveInfo returned from the package manager about this TV input service.
      * @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device.
+     * @param parentId The ID of this TV input's parent input. {@code null} if none exists.
+     * @param iconUri The {@link android.net.Uri} to load the icon image.
+     *        {@see android.content.ContentResolver#openInputStream}. If it is null, the application
+     *        icon of {@code service} will be loaded.
+     * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
+     *        will be loaded.
      * @hide
      */
+    @SystemApi
     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
-            HdmiCecDeviceInfo cecInfo, String parentId) throws XmlPullParserException, IOException {
+            HdmiCecDeviceInfo cecInfo, String parentId, String label, Uri iconUri)
+                    throws XmlPullParserException, IOException {
         return createTvInputInfo(context, service, generateInputIdForHdmiCec(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
-                cecInfo), parentId, TYPE_HDMI);
+                cecInfo), parentId, TYPE_HDMI, label, iconUri);
     }
 
     /**
@@ -158,18 +171,26 @@
      *
      * @param service The ResolveInfo returned from the package manager about this TV input service.
      * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
+     * @param iconUri The {@link android.net.Uri} to load the icon image.
+     *        {@see android.content.ContentResolver#openInputStream}. If it is null, the application
+     *        icon of {@code service} will be loaded.
+     * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
+     *        will be loaded.
      * @hide
      */
+    @SystemApi
     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
-            TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException {
+            TvInputHardwareInfo hardwareInfo, String label, Uri iconUri)
+                    throws XmlPullParserException, IOException {
         int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER);
         return createTvInputInfo(context, service, generateInputIdForHardware(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
-                hardwareInfo), null, inputType);
+                hardwareInfo), null, inputType, label, iconUri);
     }
 
     private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
-            String id, String parentId, int inputType) throws XmlPullParserException, IOException {
+            String id, String parentId, int inputType, String label, Uri iconUri)
+                    throws XmlPullParserException, IOException {
         ServiceInfo si = service.serviceInfo;
         PackageManager pm = context.getPackageManager();
         XmlResourceParser parser = null;
@@ -210,6 +231,8 @@
             }
             sa.recycle();
 
+            input.mLabel = label;
+            input.mIconUri = iconUri;
             return input;
         } catch (NameNotFoundException e) {
             throw new XmlPullParserException("Unable to create context for: " + si.packageName);
@@ -331,7 +354,11 @@
      *         a label, its name is returned.
      */
     public CharSequence loadLabel(Context context) {
-        return mService.loadLabel(context.getPackageManager());
+        if (TextUtils.isEmpty(mLabel)) {
+            return mService.loadLabel(context.getPackageManager());
+        } else {
+            return mLabel;
+        }
     }
 
     /**
@@ -343,7 +370,19 @@
      *         returned.
      */
     public Drawable loadIcon(Context context) {
-        return mService.serviceInfo.loadIcon(context.getPackageManager());
+        if (mIconUri == null) {
+            return loadDefaultIcon(context);
+        }
+        try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) {
+            Drawable drawable = Drawable.createFromStream(is, null);
+            if (drawable == null) {
+                return loadDefaultIcon(context);
+            }
+            return drawable;
+        } catch (IOException e) {
+            Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e);
+            return loadDefaultIcon(context);
+        }
     }
 
     @Override
@@ -391,6 +430,12 @@
         dest.writeString(mSetupActivity);
         dest.writeString(mSettingsActivity);
         dest.writeInt(mType);
+        dest.writeString(mIconUri == null ? null : mIconUri.toString());
+        dest.writeString(mLabel);
+    }
+
+    private Drawable loadDefaultIcon(Context context) {
+        return mService.serviceInfo.loadIcon(context.getPackageManager());
     }
 
     /**
@@ -452,5 +497,10 @@
         mSetupActivity = in.readString();
         mSettingsActivity = in.readString();
         mType = in.readInt();
+        String mIconUriString = in.readString();
+        if (mIconUriString != null) {
+            mIconUri = Uri.parse(mIconUriString);
+        }
+        mLabel = in.readString();
     }
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 5235a53..5bed40b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -126,6 +126,7 @@
      * Interface used to receive the created session.
      * @hide
      */
+    @SystemApi
     public abstract static class SessionCallback {
         /**
          * This is called after {@link TvInputManager#createSession} has been processed.
@@ -214,6 +215,7 @@
          * @param eventArgs Optional arguments of the event.
          * @hide
          */
+        @SystemApi
         public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
         }
     }
@@ -656,6 +658,7 @@
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      * @hide
      */
+    @SystemApi
     public void createSession(String inputId, final SessionCallback callback,
             Handler handler) {
         if (inputId == null) {
@@ -683,6 +686,7 @@
      * The Session provides the per-session functionality of TV inputs.
      * @hide
      */
+    @SystemApi
     public static final class Session {
         static final int DISPATCH_IN_PROGRESS = -1;
         static final int DISPATCH_NOT_HANDLED = 0;
@@ -707,7 +711,6 @@
         private InputChannel mChannel;
         private List<TvTrackInfo> mTracks;
 
-        /** @hide */
         private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
                 int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
             mToken = token;
@@ -739,7 +742,6 @@
          * Sets the {@link android.view.Surface} for this session.
          *
          * @param surface A {@link android.view.Surface} used to render video.
-         * @hide
          */
         public void setSurface(Surface surface) {
             if (mToken == null) {
@@ -763,6 +765,7 @@
          * @param height The new height of the {@link Surface}.
          * @hide
          */
+        @SystemApi
         public void dispatchSurfaceChanged(int format, int width, int height) {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 0d36e84..35dd71e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -230,6 +230,7 @@
      * @see TvInputService.Session#dispatchContentBlocked(TvContentRating)
      * @hide
      */
+    @SystemApi
     public void requestUnblockContent(TvContentRating unblockedRating) {
         if (mSession != null) {
             mSession.requestUnblockContent(unblockedRating);
@@ -622,6 +623,7 @@
          * @param eventArgs Optional arguments of the event.
          * @hide
          */
+        @SystemApi
         public void onEvent(String inputId, String eventType, Bundle eventArgs) {
         }
     }
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cf2f5d3..416cd54 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -191,7 +191,7 @@
     <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
     <string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
     <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
-    <string name="screenshot_failed_text">Couldn\'t save screenshot. Storage may be in use.</string>
+    <string name="screenshot_failed_text">Can\'t take screenshot due to limited storage space, or it isn\'t allowed by the app or your organization.</string>
 
     <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
     <string name="usb_preference_title">USB file transfer options</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index ffb4dc1..2f88e21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -99,6 +99,7 @@
     private QSPanel mQSPanel;
 
     private final Rect mClipBounds = new Rect();
+    private final StatusIconClipper mStatusIconClipper = new StatusIconClipper();
 
     public StatusBarHeaderView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -231,6 +232,7 @@
             updateAvatarScale();
             updateClockLp();
             updateBatteryLevelPaddingEnd();
+            mStatusIconClipper.update();
         }
     }
 
@@ -445,7 +447,7 @@
         mSystemIconsContainer.addView(systemIcons);
         mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
         mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
-        mSignalCluster.addOnLayoutChangeListener(mSignalClusterChanged);
+        mSignalCluster.addOnLayoutChangeListener(mStatusIconClipper);
     }
 
     public void onSystemIconsDetached() {
@@ -453,7 +455,7 @@
             mStatusIcons.setVisibility(View.VISIBLE);
         }
         if (mSignalCluster != null) {
-            mSignalCluster.removeOnLayoutChangeListener(mSignalClusterChanged);
+            mSignalCluster.removeOnLayoutChangeListener(mStatusIconClipper);
             mSignalCluster.setVisibility(View.VISIBLE);
         }
         mStatusIcons = null;
@@ -470,6 +472,7 @@
         updateMultiUserSwitch();
         updateClickTargets();
         updateBatteryLevelPaddingEnd();
+        mStatusIconClipper.update();
     }
 
     public void setUserInfoController(UserInfoController userInfoController) {
@@ -536,7 +539,7 @@
         // here.
     }
 
-    private final OnLayoutChangeListener mSignalClusterChanged = new OnLayoutChangeListener() {
+    private final class StatusIconClipper implements OnLayoutChangeListener {
         private final Rect mClipBounds = new Rect();
 
         @Override
@@ -546,9 +549,14 @@
             // they are mirrored to the real status bar.
             mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
                     mSystemIconsContainer.getHeight());
-            mSystemIconsContainer.setClipBounds(mClipBounds);
+            update();
         }
-    };
+
+        public void update() {
+            final boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+            mSystemIconsContainer.setClipBounds(onKeyguardAndCollapsed ? null : mClipBounds);
+        }
+    }
 
     private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2d6d09b..dcb4f18 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8060,8 +8060,8 @@
         boolean checkedGrants = false;
         if (checkUser) {
             // Looking for cross-user grants before enforcing the typical cross-users permissions
-            int tmpTargetUserId = unsafeConvertIncomingUser(UserHandle.getUserId(callingUid));
-            if (tmpTargetUserId != userId) {
+            int tmpTargetUserId = unsafeConvertIncomingUser(userId);
+            if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
                 if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
                     return null;
                 }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ac19bde..3fdeb54 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -413,6 +413,20 @@
         }
     }
 
+    public void noteResetAudio() {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteResetAudioLocked();
+        }
+    }
+
+    public void noteResetVideo() {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteResetVideoLocked();
+        }
+    }
+
     public void noteFlashlightOn() {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 560ee84..95cd7ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1013,12 +1013,17 @@
          }
 
         @Override
-        public void setRecordRequestListener(IHdmiRecordRequestListener listener) {
+        public void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
             // TODO: implement this.
         }
 
         @Override
-        public void startRecord(int recorderAddress, byte[] recordSource) {
+        public void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+            // TODO: implement this.
+        }
+
+        @Override
+        public void startTimerRecording(int recorderAddress, byte[] recordSource) {
             // TODO: implement this.
         }
     }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ede3dab..4304b59 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -31,6 +31,7 @@
 import android.media.session.MediaSessionInfo;
 import android.media.session.PlaybackState;
 import android.media.session.ParcelableVolumeInfo;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
@@ -105,9 +106,9 @@
     // End TransportPerformer fields
 
     // Volume handling fields
+    private AudioAttributes mAudioAttrs;
     private AudioManager mAudioManager;
     private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
-    private int mAudioStream = AudioManager.STREAM_MUSIC;
     private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
     private int mMaxVolume = 0;
     private int mCurrentVolume = 0;
@@ -131,6 +132,7 @@
         mService = service;
         mHandler = new MessageHandler(handler.getLooper());
         mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
+        mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
     }
 
     /**
@@ -219,7 +221,8 @@
             direction = -1;
         }
         if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
-            mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+            int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+            mAudioManager.adjustStreamVolume(stream, direction, flags);
         } else {
             if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                 // Nothing to do, the volume cannot be changed
@@ -245,7 +248,8 @@
 
     public void setVolumeTo(int value, int flags) {
         if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
-            mAudioManager.setStreamVolume(mAudioStream, value, flags);
+            int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+            mAudioManager.setStreamVolume(stream, value, flags);
         } else {
             if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
                 // Nothing to do. The volume can't be set directly.
@@ -315,8 +319,8 @@
      *
      * @return The audio stream the session is using.
      */
-    public int getAudioStream() {
-        return mAudioStream;
+    public AudioAttributes getAudioAttributes() {
+        return mAudioAttrs;
     }
 
     /**
@@ -613,36 +617,34 @@
         }
 
         @Override
-        public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
-            boolean typeChanged = type != mVolumeType;
-            switch(type) {
-                case MediaSession.PLAYBACK_TYPE_LOCAL:
-                    mVolumeType = type;
-                    int audioStream = arg1;
-                    if (isValidStream(audioStream)) {
-                        mAudioStream = audioStream;
-                    } else {
-                        Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
-                        mAudioStream = AudioManager.STREAM_MUSIC;
-                    }
-                    break;
-                case MediaSession.PLAYBACK_TYPE_REMOTE:
-                    mVolumeType = type;
-                    mVolumeControlType = arg1;
-                    mMaxVolume = arg2;
-                    break;
-                default:
-                    throw new IllegalArgumentException("Volume handling type " + type
-                            + " not recognized.");
+        public void setPlaybackToLocal(AudioAttributes attributes) {
+            boolean typeChanged;
+            synchronized (mLock) {
+                typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE;
+                mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
+                if (attributes != null) {
+                    mAudioAttrs = attributes;
+                } else {
+                    Log.e(TAG, "Received null audio attributes, using existing attributes");
+                }
             }
             if (typeChanged) {
                 mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
             }
         }
 
-        private boolean isValidStream(int stream) {
-            return stream >= AudioManager.STREAM_VOICE_CALL
-                    && stream <= AudioManager.STREAM_NOTIFICATION;
+        @Override
+        public void setPlaybackToRemote(int control, int max) {
+            boolean typeChanged;
+            synchronized (mLock) {
+                typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL;
+                mVolumeType = MediaSession.PLAYBACK_TYPE_REMOTE;
+                mVolumeControlType = control;
+                mMaxVolume = max;
+            }
+            if (typeChanged) {
+                mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
+            }
         }
     }
 
@@ -822,11 +824,12 @@
                     current = mOptimisticVolume != -1 ? mOptimisticVolume
                             : mCurrentVolume;
                 } else {
+                    int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
                     type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
-                    max = mAudioManager.getStreamMaxVolume(mAudioStream);
-                    current = mAudioManager.getStreamVolume(mAudioStream);
+                    max = mAudioManager.getStreamMaxVolume(stream);
+                    current = mAudioManager.getStreamVolume(stream);
                 }
-                return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+                return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current);
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/GroupedNotificationComparator.java b/services/core/java/com/android/server/notification/GroupedNotificationComparator.java
new file mode 100644
index 0000000..608d55c
--- /dev/null
+++ b/services/core/java/com/android/server/notification/GroupedNotificationComparator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.notification;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Sorts notifications, accounting for groups and sort keys.
+ */
+public class GroupedNotificationComparator extends NotificationComparator {
+    private static final String TAG = "GroupedNotificationComparator";
+
+    @Override
+    public int compare(NotificationRecord left, NotificationRecord right) {
+        // "recently intrusive" is an ad hoc group that temporarily claims noisy notifications
+        if (left.isRecentlyIntrusive() != right.isRecentlyIntrusive()) {
+            return left.isRecentlyIntrusive() ? -1 : 1;
+        }
+
+        final NotificationRecord leftProxy = left.getRankingProxy();
+        if (leftProxy == null) {
+            throw new RuntimeException("left proxy cannot be null: " + left.getKey());
+        }
+        final NotificationRecord rightProxy = right.getRankingProxy();
+        if (rightProxy == null) {
+            throw new RuntimeException("right proxy cannot be null: " + right.getKey());
+        }
+        final String leftSortKey = left.getNotification().getSortKey();
+        final String rightSortKey = right.getNotification().getSortKey();
+        if (leftProxy != rightProxy) {
+            // between groups, compare proxies
+            return Integer.compare(leftProxy.getAuthoritativeRank(),
+                    rightProxy.getAuthoritativeRank());
+        } else if (TextUtils.isEmpty(leftSortKey) || TextUtils.isEmpty(rightSortKey)) {
+            // missing sort keys, use prior rank
+            return Integer.compare(left.getAuthoritativeRank(),
+                    right.getAuthoritativeRank());
+        } else {
+            // use sort keys within group
+            return leftSortKey.compareTo(rightSortKey);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 0546a55..ec81fd2 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -18,35 +18,35 @@
 import java.util.Comparator;
 
 /**
- * Sorts notificaitons into attention-relelvant order.
+ * Sorts notifications individually into attention-relelvant order.
  */
 public class NotificationComparator
         implements Comparator<NotificationRecord> {
 
     @Override
-    public int compare(NotificationRecord lhs, NotificationRecord rhs) {
-        if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
-            return lhs.isRecentlyIntrusive() ? -1 : 1;
-        }
-        final int leftPackagePriority = lhs.getPackagePriority();
-        final int rightPackagePriority = rhs.getPackagePriority();
+    public int compare(NotificationRecord left, NotificationRecord right) {
+        final int leftPackagePriority = left.getPackagePriority();
+        final int rightPackagePriority = right.getPackagePriority();
         if (leftPackagePriority != rightPackagePriority) {
             // by priority, high to low
             return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
         }
-        final int leftScore = lhs.sbn.getScore();
-        final int rightScore = rhs.sbn.getScore();
+
+        final int leftScore = left.sbn.getScore();
+        final int rightScore = right.sbn.getScore();
         if (leftScore != rightScore) {
             // by priority, high to low
             return -1 * Integer.compare(leftScore, rightScore);
         }
-        final float leftPeple = lhs.getContactAffinity();
-        final float rightPeople = rhs.getContactAffinity();
-        if (leftPeple != rightPeople) {
+
+        final float leftPeople = left.getContactAffinity();
+        final float rightPeople = right.getContactAffinity();
+        if (leftPeople != rightPeople) {
             // by contact proximity, close to far
-            return -1 * Float.compare(leftPeple, rightPeople);
+            return -1 * Float.compare(leftPeople, rightPeople);
         }
+
         // then break ties by time, most recent first
-        return -1 * Long.compare(lhs.getRankingTimeMs(), rhs.getRankingTimeMs());
+        return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 088b813..57f3e2d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.service.notification.StatusBarNotification;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
@@ -60,7 +61,12 @@
     public boolean isUpdate;
     private int mPackagePriority;
 
-    NotificationRecord(StatusBarNotification sbn, int score)
+    // The record that ranking should use for comparisons outside the group.
+    private NotificationRecord mRankingProxy;
+    private int mAuthoritativeRank;
+
+    @VisibleForTesting
+    public NotificationRecord(StatusBarNotification sbn, int score)
     {
         this.sbn = sbn;
         this.score = score;
@@ -73,7 +79,9 @@
         mRecentlyIntrusive = previous.mRecentlyIntrusive;
         mPackagePriority = previous.mPackagePriority;
         mIntercept = previous.mIntercept;
+        mRankingProxy = previous.mRankingProxy;
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
+        // Don't copy mGroupKey, recompute it, in case it has changed
     }
 
     public Notification getNotification() { return sbn.getNotification(); }
@@ -89,6 +97,7 @@
                 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
         pw.println(prefix + "  pri=" + notification.priority + " score=" + sbn.getScore());
         pw.println(prefix + "  key=" + sbn.getKey());
+        pw.println(prefix + "  groupKey=" + getGroupKey());
         pw.println(prefix + "  contentIntent=" + notification.contentIntent);
         pw.println(prefix + "  deleteIntent=" + notification.deleteIntent);
         pw.println(prefix + "  tickerText=" + notification.tickerText);
@@ -145,6 +154,7 @@
         pw.println(prefix + "  mRecentlyIntrusive=" + mRecentlyIntrusive);
         pw.println(prefix + "  mPackagePriority=" + mPackagePriority);
         pw.println(prefix + "  mIntercept=" + mIntercept);
+        pw.println(prefix + "  mRankingProxy=" + getRankingProxy().getKey());
         pw.println(prefix + "  mRankingTimeMs=" + mRankingTimeMs);
     }
 
@@ -241,4 +251,24 @@
         }
         return sbn.getPostTime();
     }
+
+    public NotificationRecord getRankingProxy() {
+        return mRankingProxy;
+    }
+
+    public void setRankingProxy(NotificationRecord proxy) {
+        mRankingProxy = proxy;
+    }
+
+    public void setAuthoritativeRank(int authoritativeRank) {
+        mAuthoritativeRank = authoritativeRank;
+    }
+
+    public int getAuthoritativeRank() {
+        return mAuthoritativeRank;
+    }
+
+    public String getGroupKey() {
+        return sbn.getGroupKey();
+    }
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index fc03c17..d59e17b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -17,12 +17,12 @@
 
 import android.app.Notification;
 import android.content.Context;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
 import org.xmlpull.v1.XmlPullParser;
@@ -49,13 +49,13 @@
     private static final String ATT_UID = "uid";
     private static final String ATT_PRIORITY = "priority";
 
-    private static final String VALUE_HIGH = "high";
-
     private final NotificationSignalExtractor[] mSignalExtractors;
-    private final NotificationComparator mRankingComparator = new NotificationComparator();
+    private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
+    private final NotificationComparator mFinalComparator = new GroupedNotificationComparator();
 
     // Package name to uid, to priority. Would be better as Table<String, Int, Int>
     private final ArrayMap<String, SparseIntArray> mPackagePriorities;
+    private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp;
 
     private final Context mContext;
     private final Handler mRankingHandler;
@@ -83,6 +83,7 @@
                 Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
             }
         }
+        mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
     }
 
     public void extractSignals(NotificationRecord r) {
@@ -166,11 +167,39 @@
     }
 
     public void sort(ArrayList<NotificationRecord> notificationList) {
-        Collections.sort(notificationList, mRankingComparator);
+        final int N = notificationList.size();
+        // clear group proxies
+        for (int i = N - 1; i >= 0; i--) {
+            notificationList.get(i).setRankingProxy(null);
+        }
+
+        // rank each record individually
+        Collections.sort(notificationList, mPreliminaryComparator);
+
+        // record inidivdual ranking result and nominate proxies for each group
+        for (int i = N - 1; i >= 0; i--) {
+            final NotificationRecord record = notificationList.get(i);
+            record.setAuthoritativeRank(i);
+            final String groupKey = record.getGroupKey();
+            boolean isGroupSummary = record.getNotification().getGroup() != null
+                    && (record.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+            if (isGroupSummary || mProxyByGroupTmp.get(groupKey) == null) {
+                mProxyByGroupTmp.put(groupKey, record);
+            }
+        }
+        // assign nominated proxies to each notification
+        for (int i = 0; i < N; i++) {
+            final NotificationRecord record = notificationList.get(i);
+            record.setRankingProxy(mProxyByGroupTmp.get(record.getGroupKey()));
+        }
+        // Do a second ranking pass, using group proxies
+        Collections.sort(notificationList, mFinalComparator);
+
+        mProxyByGroupTmp.clear();
     }
 
     public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
-        return Collections.binarySearch(notificationList, target, mRankingComparator);
+        return Collections.binarySearch(notificationList, target, mFinalComparator);
     }
 
     private static int safeInt(XmlPullParser parser, String att, int defValue) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 01aaca0..d7ecd7a 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -993,7 +993,10 @@
                 synchronized (mLock) {
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
-
+                        if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) {
+                            // Do not log the watch history for passthrough inputs.
+                            return;
+                        }
                         long currentTime = System.currentTimeMillis();
                         long channelId = ContentUris.parseId(channelUri);
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index aae2bb8..eac2819 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.*;
 
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import android.app.admin.DevicePolicyManager;
 import android.app.AppOpsManager;
 import android.util.ArraySet;
 import android.util.TimeUtils;
@@ -425,6 +426,13 @@
      */
     WindowState[] mRebuildTmp = new WindowState[20];
 
+    /**
+     * Stores for each user whether screencapture is disabled
+     * This array is essentially a cache for all userId for
+     * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+     */
+    SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
+
     IInputMethodManager mInputMethodManager;
 
     AccessibilityController mAccessibilityController;
@@ -2439,6 +2447,45 @@
         return res;
     }
 
+    /**
+     * Returns whether screen capture is disabled for all windows of a specific user.
+     */
+    boolean isScreenCaptureDisabledLocked(int userId) {
+        Boolean disabled = mScreenCaptureDisabled.get(userId);
+        if (disabled != null) {
+            return disabled;
+        }
+
+        // mScreenCaptureDisabled not set yet, try to update it.
+        updateScreenCaptureDisabledLocked(userId);
+        disabled = mScreenCaptureDisabled.get(userId);
+        if (disabled == null) {
+            // Not able to update, return false by default.
+            return false;
+        } else {
+            return disabled;
+        }
+    }
+
+    /**
+     * Update mScreenCaptureDisabled for specific user according to the device policy manager.
+     */
+    @Override
+    public void updateScreenCaptureDisabled(int userId) {
+        mH.sendMessage(mH.obtainMessage(H.UPDATE_SCRN_CAP, userId, 0 /* unused argument */));
+    }
+
+    void updateScreenCaptureDisabledLocked(int userId) {
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext
+                .getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (dpm != null) {
+            boolean disabled = dpm.getScreenCaptureDisabled(null, userId);
+            mScreenCaptureDisabled.put(userId, disabled);
+        } else {
+            Slog.e(TAG, "Could not get DevicePolicyManager.");
+        }
+    }
+
     public void removeWindow(Session session, IWindow client) {
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client, false);
@@ -7203,6 +7250,8 @@
 
         public static final int NEW_ANIMATOR_SCALE = 34;
 
+        public static final int UPDATE_SCRN_CAP = 35;
+
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7677,6 +7726,13 @@
                     }
                 }
                 break;
+
+                case UPDATE_SCRN_CAP: {
+                    synchronized (mWindowMap) {
+                        updateScreenCaptureDisabledLocked(msg.arg1);
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 49d4ae9..f3afe82 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -39,6 +39,7 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Debug;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -690,6 +691,10 @@
                 flags |= SurfaceControl.SECURE;
             }
 
+            if (mService.isScreenCaptureDisabledLocked(UserHandle.getUserId(mWin.mOwnerUid))) {
+                flags |= SurfaceControl.SECURE;
+            }
+
             int width;
             int height;
             if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5c661af..e0612eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -258,6 +258,7 @@
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
         private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+        private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
         private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
         private static final String TAG_ACCOUNT_TYPE = "account-type";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
@@ -326,6 +327,8 @@
         boolean encryptionRequested = false;
         boolean disableCamera = false;
         boolean disableCallerId = false;
+        boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
+
         Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // TODO: review implementation decisions with frameworks team
@@ -443,6 +446,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
                 out.endTag(null, TAG_DISABLE_CALLER_ID);
             }
+            if (disableScreenCapture) {
+                out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture));
+                out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+            }
             if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
                 out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
                 out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -528,6 +536,9 @@
                 } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
                     disableCallerId = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
+                    disableScreenCapture = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
                     disabledKeyguardFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -606,6 +617,8 @@
                     pw.println(disableCamera);
             pw.print(prefix); pw.print("disableCallerId=");
                     pw.println(disableCallerId);
+            pw.print(prefix); pw.print("disableScreenCapture=");
+                    pw.println(disableScreenCapture);
             pw.print(prefix); pw.print("disabledKeyguardFeatures=");
                     pw.println(disabledKeyguardFeatures);
         }
@@ -2977,6 +2990,58 @@
     private void setEncryptionRequested(boolean encrypt) {
     }
 
+
+    /**
+     * Set whether the screen capture is disabled for the user managed by the specified admin.
+     */
+    public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (ap.disableScreenCapture != disabled) {
+                ap.disableScreenCapture = disabled;
+                saveSettingsLocked(userHandle);
+                try {
+                    getWindowManager().updateScreenCaptureDisabled(userHandle);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether or not screen capture is disabled for a given admin, or disabled for any
+     * active admin (if given admin is null).
+     */
+    public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) {
+        if (!mHasFeature) {
+            return false;
+        }
+        synchronized (this) {
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+                return (admin != null) ? admin.disableScreenCapture : false;
+            }
+
+            DevicePolicyData policy = getUserData(userHandle);
+            final int N = policy.mAdminList.size();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = policy.mAdminList.get(i);
+                if (admin.disableScreenCapture) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     /**
      * The system property used to share the state of the camera. The native camera service
      * is expected to read this property and act accordingly.
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
new file mode 100644
index 0000000..3cc04e8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+
+public class RankingHelperTest extends AndroidTestCase {
+
+    private Notification mNotiGroupGSortA;
+    private Notification mNotiGroupGSortB;
+    private Notification mNotiNoGroup;
+    private Notification mNotiNoGroup2;
+    private Notification mNotiNoGroupSortA;
+    private NotificationRecord mRecordGroupGSortA;
+    private NotificationRecord mRecordGroupGSortB;
+    private NotificationRecord mRecordNoGroup;
+    private NotificationRecord mRecordNoGroup2;
+    private NotificationRecord mRecordNoGroupSortA;
+    private RankingHelper mHelper;
+
+    @Override
+    public void setUp() {
+        UserHandle user = UserHandle.ALL;
+
+        mHelper = new RankingHelper(getContext(), null, new String[0]);
+
+        mNotiGroupGSortA = new Notification.Builder(getContext())
+                .setContentTitle("A")
+                .setGroup("G")
+                .setSortKey("A")
+                .setWhen(1205)
+                .build();
+        mRecordGroupGSortA = new NotificationRecord(new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 0);
+
+        mNotiGroupGSortB = new Notification.Builder(getContext())
+                .setContentTitle("B")
+                .setGroup("G")
+                .setSortKey("B")
+                .setWhen(1200)
+                .build();
+        mRecordGroupGSortB = new NotificationRecord(new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 0);
+
+        mNotiNoGroup = new Notification.Builder(getContext())
+                .setContentTitle("C")
+                .setWhen(1201)
+                .build();
+        mRecordNoGroup = new NotificationRecord(new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 0);
+
+        mNotiNoGroup2 = new Notification.Builder(getContext())
+                .setContentTitle("D")
+                .setWhen(1202)
+                .build();
+        mRecordNoGroup2 = new NotificationRecord(new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 0);
+
+        mNotiNoGroupSortA = new Notification.Builder(getContext())
+                .setContentTitle("E")
+                .setWhen(1201)
+                .setSortKey("A")
+                .build();
+        mRecordNoGroupSortA = new NotificationRecord(new StatusBarNotification(
+                "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 0);
+    }
+
+    @SmallTest
+    public void testFindAfterRankingWithASplitGroup() throws Exception {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
+        notificationList.add(mRecordGroupGSortA);
+        notificationList.add(mRecordGroupGSortB);
+        notificationList.add(mRecordNoGroup);
+        notificationList.add(mRecordNoGroupSortA);
+        mHelper.sort(notificationList);
+        assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0);
+        assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0);
+        assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0);
+        assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0);
+    }
+
+    @SmallTest
+    public void testSortShouldNotThrowWithPlainNotifications() throws Exception {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
+        notificationList.add(mRecordNoGroup);
+        notificationList.add(mRecordNoGroup2);
+        mHelper.sort(notificationList);
+    }
+
+    @SmallTest
+    public void testSortShouldNotThrowOneSorted() throws Exception {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
+        notificationList.add(mRecordNoGroup);
+        notificationList.add(mRecordNoGroupSortA);
+        mHelper.sort(notificationList);
+    }
+
+    @SmallTest
+    public void testSortShouldNotThrowOneNotification() throws Exception {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
+        notificationList.add(mRecordNoGroup);
+        mHelper.sort(notificationList);
+    }
+
+    @SmallTest
+    public void testSortShouldNotThrowOneSortKey() throws Exception {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
+        notificationList.add(mRecordGroupGSortB);
+        mHelper.sort(notificationList);
+    }
+
+    @SmallTest
+    public void testSortShouldNotThrowOnEmptyList() throws Exception {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
+        mHelper.sort(notificationList);
+    }
+}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 5a7753c..1f9071e 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -71,8 +71,8 @@
     private final String mSubscriptionNumber;
     private final int mCapabilities;
     private final int mIconResId;
-    private final String mLabel;
-    private final String mShortDescription;
+    private final CharSequence mLabel;
+    private final CharSequence mShortDescription;
     private boolean mVideoCallingSupported;
 
     public PhoneAccount(
@@ -81,8 +81,8 @@
             String subscriptionNumber,
             int capabilities,
             int iconResId,
-            String label,
-            String shortDescription,
+            CharSequence label,
+            CharSequence shortDescription,
             boolean supportsVideoCalling) {
         mAccountHandle = account;
         mHandle = handle;
@@ -136,11 +136,11 @@
     }
 
     /**
-     * A short string label describing a {@code PhoneAccount}.
+     * A short label describing a {@code PhoneAccount}.
      *
      * @return A label for this {@code PhoneAccount}.
      */
-    public String getLabel() {
+    public CharSequence getLabel() {
         return mLabel;
     }
 
@@ -149,7 +149,7 @@
      *
      * @return A description for this {@code PhoneAccount}.
      */
-    public String getShortDescription() {
+    public CharSequence getShortDescription() {
         return mShortDescription;
     }
 
@@ -214,8 +214,8 @@
         out.writeString(mSubscriptionNumber);
         out.writeInt(mCapabilities);
         out.writeInt(mIconResId);
-        out.writeString(mLabel);
-        out.writeString(mShortDescription);
+        out.writeCharSequence(mLabel);
+        out.writeCharSequence(mShortDescription);
         out.writeInt(mVideoCallingSupported ? 1 : 0);
     }
 
@@ -238,8 +238,8 @@
         mSubscriptionNumber = in.readString();
         mCapabilities = in.readInt();
         mIconResId = in.readInt();
-        mLabel = in.readString();
-        mShortDescription = in.readString();
+        mLabel = in.readCharSequence();
+        mShortDescription = in.readCharSequence();
         mVideoCallingSupported = in.readInt() == 1;
     }
 }
diff --git a/telecomm/java/android/telecomm/StatusHints.java b/telecomm/java/android/telecomm/StatusHints.java
index 5e64bff..50f525a 100644
--- a/telecomm/java/android/telecomm/StatusHints.java
+++ b/telecomm/java/android/telecomm/StatusHints.java
@@ -34,11 +34,11 @@
 public final class StatusHints implements Parcelable {
 
     private final ComponentName mComponentName;
-    private final String mLabel;
+    private final CharSequence mLabel;
     private final int mIconId;
     private final Bundle mExtras;
 
-    public StatusHints(ComponentName componentName, String label, int iconId, Bundle extras) {
+    public StatusHints(ComponentName componentName, CharSequence label, int iconId, Bundle extras) {
         mComponentName = componentName;
         mLabel = label;
         mIconId = iconId;
@@ -55,7 +55,7 @@
     /**
      * @return The label displayed in the in-call UI.
      */
-    public String getLabel() {
+    public CharSequence getLabel() {
         return mLabel;
     }
 
@@ -88,7 +88,7 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeParcelable(mComponentName, flags);
-        out.writeString(mLabel);
+        out.writeCharSequence(mLabel);
         out.writeInt(mIconId);
         out.writeParcelable(mExtras, 0);
     }
@@ -106,7 +106,7 @@
 
     private StatusHints(Parcel in) {
         mComponentName = in.readParcelable(getClass().getClassLoader());
-        mLabel = in.readString();
+        mLabel = in.readCharSequence();
         mIconId = in.readInt();
         mExtras = (Bundle) in.readParcelable(getClass().getClassLoader());
     }
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6927b26..2ffe4a2 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -360,6 +360,11 @@
     }
 
     @Override
+    public void updateScreenCaptureDisabled(int userId) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void updateRotation(boolean arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
     }