Merge "Add comments for RemoteCallVideoClient and RemoteCallVideoProvider." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 7a8907e..79500ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -148,6 +148,7 @@
 	core/java/android/hardware/ISerialManager.aidl \
 	core/java/android/hardware/display/IDisplayManager.aidl \
 	core/java/android/hardware/display/IDisplayManagerCallback.aidl \
+	core/java/android/hardware/display/IVirtualDisplayCallbacks.aidl \
 	core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
 	core/java/android/hardware/hdmi/IHdmiControlService.aidl \
 	core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \
@@ -325,6 +326,9 @@
 	media/java/android/media/routing/IMediaRouterDelegate.aidl \
 	media/java/android/media/routing/IMediaRouterRoutingCallback.aidl \
 	media/java/android/media/routing/IMediaRouterStateCallback.aidl \
+	media/java/android/media/projection/IMediaProjection.aidl \
+	media/java/android/media/projection/IMediaProjectionCallback.aidl \
+	media/java/android/media/projection/IMediaProjectionManager.aidl \
 	media/java/android/media/session/IActiveSessionsListener.aidl \
 	media/java/android/media/session/ISessionController.aidl \
 	media/java/android/media/session/ISessionControllerCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index 633d0b2..6f7f9ee 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -106,7 +106,7 @@
     field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
     field public static final java.lang.String READ_PROFILE = "android.permission.READ_PROFILE";
     field public static final java.lang.String READ_SMS = "android.permission.READ_SMS";
-    field public static final java.lang.String READ_SOCIAL_STREAM = "android.permission.READ_SOCIAL_STREAM";
+    field public static final deprecated java.lang.String READ_SOCIAL_STREAM = "android.permission.READ_SOCIAL_STREAM";
     field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
     field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
     field public static final java.lang.String READ_USER_DICTIONARY = "android.permission.READ_USER_DICTIONARY";
@@ -157,7 +157,7 @@
     field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
     field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
     field public static final java.lang.String WRITE_SMS = "android.permission.WRITE_SMS";
-    field public static final java.lang.String WRITE_SOCIAL_STREAM = "android.permission.WRITE_SOCIAL_STREAM";
+    field public static final deprecated java.lang.String WRITE_SOCIAL_STREAM = "android.permission.WRITE_SOCIAL_STREAM";
     field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
     field public static final java.lang.String WRITE_USER_DICTIONARY = "android.permission.WRITE_USER_DICTIONARY";
   }
@@ -4642,7 +4642,7 @@
     field public android.app.Notification publicVersion;
     field public android.net.Uri sound;
     field public java.lang.CharSequence tickerText;
-    field public android.widget.RemoteViews tickerView;
+    field public deprecated android.widget.RemoteViews tickerView;
     field public long[] vibrate;
     field public int visibility;
     field public long when;
@@ -4743,7 +4743,7 @@
     method public android.app.Notification.Builder setStyle(android.app.Notification.Style);
     method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
     method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
-    method public android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
     method public android.app.Notification.Builder setUsesChronometer(boolean);
     method public android.app.Notification.Builder setVibrate(long[]);
     method public android.app.Notification.Builder setVisibility(int);
@@ -6185,10 +6185,14 @@
     method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
     method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
     method public boolean readRemoteRssi();
+    method public boolean requestConnectionParameterUpdate(int);
     method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
     method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
     method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    field public static final int GATT_CONNECTION_BALANCED = 0; // 0x0
     field public static final int GATT_CONNECTION_CONGESTED = 143; // 0x8f
+    field public static final int GATT_CONNECTION_HIGH_PRIORITY = 1; // 0x1
+    field public static final int GATT_CONNECTION_LOW_POWER = 2; // 0x2
     field public static final int GATT_FAILURE = 257; // 0x101
     field public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5; // 0x5
     field public static final int GATT_INSUFFICIENT_ENCRYPTION = 15; // 0xf
@@ -7191,6 +7195,7 @@
     field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
     field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     field public static final java.lang.String LOCATION_SERVICE = "location";
+    field public static final java.lang.String MEDIA_PROJECTION_SERVICE = "media_projection";
     field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final java.lang.String MEDIA_SESSION_SERVICE = "media_session";
     field public static final int MODE_APPEND = 32768; // 0x8000
@@ -7213,6 +7218,7 @@
     field public static final java.lang.String TELEPHONY_SERVICE = "phone";
     field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
     field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
+    field public static final java.lang.String TV_PARENTAL_CONTROL_SERVICE = "tv_parental_control";
     field public static final java.lang.String UI_MODE_SERVICE = "uimode";
     field public static final java.lang.String USB_SERVICE = "usb";
     field public static final java.lang.String USER_SERVICE = "user";
@@ -13107,6 +13113,7 @@
 
   public final class DisplayManager {
     method public android.hardware.display.VirtualDisplay createVirtualDisplay(java.lang.String, int, int, int, android.view.Surface, int);
+    method public android.hardware.display.VirtualDisplay createVirtualDisplay(java.lang.String, int, int, int, android.view.Surface, int, android.hardware.display.VirtualDisplay.Callbacks, android.os.Handler);
     method public android.view.Display getDisplay(int);
     method public android.view.Display[] getDisplays();
     method public android.view.Display[] getDisplays(java.lang.String);
@@ -13116,6 +13123,7 @@
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
     field public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1; // 0x1
+    field public static final int VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE = 16; // 0x10
     field public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 4; // 0x4
   }
 
@@ -13132,6 +13140,13 @@
     method public void setSurface(android.view.Surface);
   }
 
+  public static abstract class VirtualDisplay.Callbacks {
+    ctor public VirtualDisplay.Callbacks();
+    method public void onDisplayPaused();
+    method public void onDisplayResumed();
+    method public void onDisplayStopped();
+  }
+
 }
 
 package android.hardware.input {
@@ -13953,6 +13968,29 @@
     method public android.media.AudioAttributes.Builder setUsage(int);
   }
 
+  public class AudioDevice {
+    field public static final int DEVICE_TYPE_AUX_LINE = 19; // 0x13
+    field public static final int DEVICE_TYPE_BLUETOOTH_A2DP = 8; // 0x8
+    field public static final int DEVICE_TYPE_BLUETOOTH_SCO = 7; // 0x7
+    field public static final int DEVICE_TYPE_BUILTIN_EARPIECE = 1; // 0x1
+    field public static final int DEVICE_TYPE_BUILTIN_MIC = 15; // 0xf
+    field public static final int DEVICE_TYPE_BUILTIN_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_DOCK = 13; // 0xd
+    field public static final int DEVICE_TYPE_FM = 14; // 0xe
+    field public static final int DEVICE_TYPE_FM_TUNER = 16; // 0x10
+    field public static final int DEVICE_TYPE_HDMI = 9; // 0x9
+    field public static final int DEVICE_TYPE_HDMI_ARC = 10; // 0xa
+    field public static final int DEVICE_TYPE_LINE_ANALOG = 5; // 0x5
+    field public static final int DEVICE_TYPE_LINE_DIGITAL = 6; // 0x6
+    field public static final int DEVICE_TYPE_TELEPHONY = 18; // 0x12
+    field public static final int DEVICE_TYPE_TV_TUNER = 17; // 0x11
+    field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int DEVICE_TYPE_USB_ACCESSORY = 12; // 0xc
+    field public static final int DEVICE_TYPE_USB_DEVICE = 11; // 0xb
+    field public static final int DEVICE_TYPE_WIRED_HEADPHONES = 4; // 0x4
+    field public static final int DEVICE_TYPE_WIRED_HEADSET = 3; // 0x3
+  }
+
   public class AudioFormat {
     field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
     field public static final deprecated int CHANNEL_CONFIGURATION_INVALID = 0; // 0x0
@@ -15885,8 +15923,11 @@
 
   public class Virtualizer extends android.media.audiofx.AudioEffect {
     ctor public Virtualizer(int, int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException;
+    method public boolean canVirtualize(int, int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
+    method public boolean forceVirtualizationMode(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
     method public android.media.audiofx.Virtualizer.Settings getProperties() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
     method public short getRoundedStrength() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
+    method public boolean getSpeakerAngles(int, int, int[]) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
     method public boolean getStrengthSupported();
     method public void setParameterListener(android.media.audiofx.Virtualizer.OnParameterChangeListener);
     method public void setProperties(android.media.audiofx.Virtualizer.Settings) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException;
@@ -16008,6 +16049,28 @@
 
 }
 
+package android.media.projection {
+
+  public final class MediaProjection {
+    method public void addCallback(android.media.projection.MediaProjection.Callback, android.os.Handler);
+    method public android.media.AudioRecord createAudioRecord(int, int, int, int);
+    method public android.hardware.display.VirtualDisplay createVirtualDisplay(java.lang.String, int, int, int, boolean, android.view.Surface, android.hardware.display.VirtualDisplay.Callbacks, android.os.Handler);
+    method public void removeCallback(android.media.projection.MediaProjection.Callback);
+    method public void stop();
+  }
+
+  public static abstract class MediaProjection.Callback {
+    ctor public MediaProjection.Callback();
+    method public void onStop();
+  }
+
+  public final class MediaProjectionManager {
+    method public android.media.projection.MediaProjection getMediaProjection(int, android.content.Intent);
+    method public android.content.Intent getScreenCaptureIntent();
+  }
+
+}
+
 package android.media.routing {
 
   public final class MediaRouteSelector implements android.os.Parcelable {
@@ -16402,10 +16465,12 @@
 
 package android.media.tv {
 
-  public class TvContentRating {
+  public final class TvContentRating {
     ctor public TvContentRating(java.lang.String);
     ctor public TvContentRating(java.lang.String, java.lang.String[]);
     method public java.lang.String flattenToString();
+    method public java.lang.String getMainRating();
+    method public java.util.List<java.lang.String> getSubRatings();
     method public static android.media.tv.TvContentRating unflattenFromString(java.lang.String);
     field public static final java.lang.String RATING_KR_12 = "RATING_KR_12";
     field public static final java.lang.String RATING_KR_15 = "RATING_KR_15";
@@ -16429,8 +16494,9 @@
     method public static final android.net.Uri buildChannelLogoUri(long);
     method public static final android.net.Uri buildChannelLogoUri(android.net.Uri);
     method public static final android.net.Uri buildChannelUri(long);
+    method public static final android.net.Uri buildChannelUriForPassthroughTvInput(java.lang.String);
     method public static final android.net.Uri buildChannelsUriForInput(java.lang.String);
-    method public static final android.net.Uri buildChannelsUriForInput(java.lang.String, boolean);
+    method public static final java.lang.String buildInputId(android.content.ComponentName);
     method public static final android.net.Uri buildProgramUri(long);
     method public static final android.net.Uri buildProgramsUriForChannel(long);
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
@@ -16445,7 +16511,6 @@
 
   public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
     method public static final java.lang.String getVideoResolution(java.lang.String);
-    field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
     field public static final java.lang.String COLUMN_CONDITIONAL_ACCESS = "conditional_access";
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
@@ -16467,31 +16532,30 @@
     field public static final int SERVICE_TYPE_AUDIO = 2; // 0x2
     field public static final int SERVICE_TYPE_AUDIO_VIDEO = 1; // 0x1
     field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
-    field public static final int TYPE_1SEG = 263168; // 0x40400
-    field public static final int TYPE_ATSC_C = 197120; // 0x30200
-    field public static final int TYPE_ATSC_M_H = 197376; // 0x30300
-    field public static final int TYPE_ATSC_T = 196608; // 0x30000
-    field public static final int TYPE_CMMB = 327936; // 0x50100
-    field public static final int TYPE_DTMB = 327680; // 0x50000
-    field public static final int TYPE_DVB_C = 131584; // 0x20200
-    field public static final int TYPE_DVB_C2 = 131585; // 0x20201
-    field public static final int TYPE_DVB_H = 131840; // 0x20300
-    field public static final int TYPE_DVB_S = 131328; // 0x20100
-    field public static final int TYPE_DVB_S2 = 131329; // 0x20101
-    field public static final int TYPE_DVB_SH = 132096; // 0x20400
-    field public static final int TYPE_DVB_T = 131072; // 0x20000
-    field public static final int TYPE_DVB_T2 = 131073; // 0x20001
-    field public static final int TYPE_ISDB_C = 262912; // 0x40300
-    field public static final int TYPE_ISDB_S = 262656; // 0x40200
-    field public static final int TYPE_ISDB_T = 262144; // 0x40000
-    field public static final int TYPE_ISDB_TB = 262400; // 0x40100
+    field public static final int TYPE_1SEG = 197632; // 0x30400
+    field public static final int TYPE_ATSC_C = 131584; // 0x20200
+    field public static final int TYPE_ATSC_M_H = 131840; // 0x20300
+    field public static final int TYPE_ATSC_T = 131072; // 0x20000
+    field public static final int TYPE_CMMB = 262400; // 0x40100
+    field public static final int TYPE_DTMB = 262144; // 0x40000
+    field public static final int TYPE_DVB_C = 66048; // 0x10200
+    field public static final int TYPE_DVB_C2 = 66049; // 0x10201
+    field public static final int TYPE_DVB_H = 66304; // 0x10300
+    field public static final int TYPE_DVB_S = 65792; // 0x10100
+    field public static final int TYPE_DVB_S2 = 65793; // 0x10101
+    field public static final int TYPE_DVB_SH = 66560; // 0x10400
+    field public static final int TYPE_DVB_T = 65536; // 0x10000
+    field public static final int TYPE_DVB_T2 = 65537; // 0x10001
+    field public static final int TYPE_ISDB_C = 197376; // 0x30300
+    field public static final int TYPE_ISDB_S = 197120; // 0x30200
+    field public static final int TYPE_ISDB_T = 196608; // 0x30000
+    field public static final int TYPE_ISDB_TB = 196864; // 0x30100
     field public static final int TYPE_NTSC = 1; // 0x1
     field public static final int TYPE_OTHER = 0; // 0x0
     field public static final int TYPE_PAL = 2; // 0x2
-    field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000
     field public static final int TYPE_SECAM = 3; // 0x3
-    field public static final int TYPE_S_DMB = 393472; // 0x60100
-    field public static final int TYPE_T_DMB = 393216; // 0x60000
+    field public static final int TYPE_S_DMB = 327936; // 0x50100
+    field public static final int TYPE_T_DMB = 327680; // 0x50000
     field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
     field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
     field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
@@ -16560,21 +16624,30 @@
     method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
     method public int getType();
-    method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
-    method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
+    method public boolean isPassthroughInputType();
+    method public android.graphics.drawable.Drawable loadIcon(android.content.Context);
+    method public java.lang.CharSequence loadLabel(android.content.Context);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String EXTRA_INPUT_ID = "inputId";
-    field public static final int TYPE_HDMI = 1; // 0x1
-    field public static final int TYPE_PASSTHROUGH = 3; // 0x3
+    field public static final int TYPE_COMPONENT = 8; // 0x8
+    field public static final int TYPE_COMPOSITE = 9; // 0x9
+    field public static final int TYPE_DISPLAY_PORT = 4; // 0x4
+    field public static final int TYPE_DVI = 6; // 0x6
+    field public static final int TYPE_HDMI = 3; // 0x3
+    field public static final int TYPE_OTHER_HARDWARE = 1; // 0x1
+    field public static final int TYPE_SCART = 5; // 0x5
+    field public static final int TYPE_SVIDEO = 10; // 0xa
     field public static final int TYPE_TUNER = 2; // 0x2
+    field public static final int TYPE_VGA = 7; // 0x7
     field public static final int TYPE_VIRTUAL = 0; // 0x0
   }
 
   public final class TvInputManager {
     method public int getInputState(java.lang.String);
+    method public android.media.tv.TvInputInfo getTvInputInfo(java.lang.String);
     method public java.util.List<android.media.tv.TvInputInfo> getTvInputList();
-    method public void registerListener(android.media.tv.TvInputManager.TvInputListener, android.os.Handler);
-    method public void unregisterListener(android.media.tv.TvInputManager.TvInputListener);
+    method public void registerCallback(android.media.tv.TvInputManager.TvInputCallback, android.os.Handler);
+    method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback);
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -16584,15 +16657,17 @@
     field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2
   }
 
-  public static abstract class TvInputManager.TvInputListener {
-    ctor public TvInputManager.TvInputListener();
+  public static abstract class TvInputManager.TvInputCallback {
+    ctor public TvInputManager.TvInputCallback();
+    method public void onInputAdded(java.lang.String);
+    method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
   }
 
   public abstract class TvInputService extends android.app.Service {
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract android.media.tv.TvInputService.Session onCreateSession();
+    method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -16600,9 +16675,11 @@
   public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session();
     method public void dispatchChannelRetuned(android.net.Uri);
+    method public void dispatchContentBlocked(android.media.tv.TvContentRating);
     method public void dispatchTrackInfoChanged(java.util.List<android.media.tv.TvTrackInfo>);
     method public void dispatchVideoAvailable();
     method public void dispatchVideoUnavailable(int);
+    method public abstract void onContentUnblocked(android.media.tv.TvContentRating);
     method public android.view.View onCreateOverlayView();
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -16614,6 +16691,7 @@
     method public abstract void onSetCaptionEnabled(boolean);
     method public abstract void onSetStreamVolume(float);
     method public abstract boolean onSetSurface(android.view.Surface);
+    method public void onSurfaceChanged(int, int, int);
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
     method public abstract boolean onTune(android.net.Uri);
@@ -16621,6 +16699,19 @@
     method public void setOverlayViewEnabled(boolean);
   }
 
+  public final class TvParentalControlManager {
+    method public void addParentalControlCallback(android.media.tv.TvParentalControlManager.ParentalControlCallback, android.os.Handler);
+    method public final boolean isEnabled();
+    method public final boolean isRatingBlocked(android.media.tv.TvContentRating);
+    method public void removeParentalControlCallback(android.media.tv.TvParentalControlManager.ParentalControlCallback);
+  }
+
+  public static abstract class TvParentalControlManager.ParentalControlCallback {
+    ctor public TvParentalControlManager.ParentalControlCallback();
+    method public void onBlockedRatingsChanged();
+    method public void onEnabledChanged(boolean);
+  }
+
   public final class TvTrackInfo implements android.os.Parcelable {
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
@@ -16678,6 +16769,7 @@
   public static abstract class TvView.TvInputListener {
     ctor public TvView.TvInputListener();
     method public void onChannelRetuned(java.lang.String, android.net.Uri);
+    method public void onContentBlocked(java.lang.String, android.media.tv.TvContentRating);
     method public void onError(java.lang.String, int);
     method public void onTrackInfoChanged(java.lang.String, java.util.List<android.media.tv.TvTrackInfo>);
     method public void onVideoAvailable(java.lang.String);
@@ -24185,8 +24277,8 @@
     field public static final java.lang.String PHOTO_FILE_ID = "data14";
   }
 
-  public static final class ContactsContract.Contacts.StreamItems implements android.provider.ContactsContract.StreamItemsColumns {
-    field public static final java.lang.String CONTENT_DIRECTORY = "stream_items";
+  public static final deprecated class ContactsContract.Contacts.StreamItems implements android.provider.ContactsContract.StreamItemsColumns {
+    field public static final deprecated java.lang.String CONTENT_DIRECTORY = "stream_items";
   }
 
   protected static abstract interface ContactsContract.ContactsColumns {
@@ -24482,8 +24574,8 @@
     field public static final java.lang.String DATA_ID = "data_id";
   }
 
-  public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
-    field public static final java.lang.String CONTENT_DIRECTORY = "stream_items";
+  public static final deprecated class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
+    field public static final deprecated java.lang.String CONTENT_DIRECTORY = "stream_items";
   }
 
   protected static abstract interface ContactsContract.RawContactsColumns {
@@ -24557,54 +24649,54 @@
     field public static final android.net.Uri PROFILE_CONTENT_URI;
   }
 
-  public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
-    field public static final java.lang.String PHOTO = "photo";
+  public static final deprecated class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
+    field public static final deprecated java.lang.String PHOTO = "photo";
   }
 
-  protected static abstract interface ContactsContract.StreamItemPhotosColumns {
-    field public static final java.lang.String PHOTO_FILE_ID = "photo_file_id";
-    field public static final java.lang.String PHOTO_URI = "photo_uri";
-    field public static final java.lang.String SORT_INDEX = "sort_index";
-    field public static final java.lang.String STREAM_ITEM_ID = "stream_item_id";
-    field public static final java.lang.String SYNC1 = "stream_item_photo_sync1";
-    field public static final java.lang.String SYNC2 = "stream_item_photo_sync2";
-    field public static final java.lang.String SYNC3 = "stream_item_photo_sync3";
-    field public static final java.lang.String SYNC4 = "stream_item_photo_sync4";
+  protected static abstract deprecated interface ContactsContract.StreamItemPhotosColumns {
+    field public static final deprecated java.lang.String PHOTO_FILE_ID = "photo_file_id";
+    field public static final deprecated java.lang.String PHOTO_URI = "photo_uri";
+    field public static final deprecated java.lang.String SORT_INDEX = "sort_index";
+    field public static final deprecated java.lang.String STREAM_ITEM_ID = "stream_item_id";
+    field public static final deprecated java.lang.String SYNC1 = "stream_item_photo_sync1";
+    field public static final deprecated java.lang.String SYNC2 = "stream_item_photo_sync2";
+    field public static final deprecated java.lang.String SYNC3 = "stream_item_photo_sync3";
+    field public static final deprecated java.lang.String SYNC4 = "stream_item_photo_sync4";
   }
 
-  public static final class ContactsContract.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
-    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item";
-    field public static final android.net.Uri CONTENT_LIMIT_URI;
-    field public static final android.net.Uri CONTENT_PHOTO_URI;
-    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
-    field public static final android.net.Uri CONTENT_URI;
-    field public static final java.lang.String MAX_ITEMS = "max_items";
+  public static final deprecated class ContactsContract.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
+    field public static final deprecated java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item";
+    field public static final deprecated android.net.Uri CONTENT_LIMIT_URI;
+    field public static final deprecated android.net.Uri CONTENT_PHOTO_URI;
+    field public static final deprecated java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
+    field public static final deprecated android.net.Uri CONTENT_URI;
+    field public static final deprecated java.lang.String MAX_ITEMS = "max_items";
   }
 
-  public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
-    field public static final java.lang.String CONTENT_DIRECTORY = "photo";
-    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo";
-    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
+  public static final deprecated class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
+    field public static final deprecated java.lang.String CONTENT_DIRECTORY = "photo";
+    field public static final deprecated java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo";
+    field public static final deprecated java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
   }
 
-  protected static abstract interface ContactsContract.StreamItemsColumns {
-    field public static final java.lang.String ACCOUNT_NAME = "account_name";
-    field public static final java.lang.String ACCOUNT_TYPE = "account_type";
-    field public static final java.lang.String COMMENTS = "comments";
-    field public static final java.lang.String CONTACT_ID = "contact_id";
-    field public static final java.lang.String CONTACT_LOOKUP_KEY = "contact_lookup";
-    field public static final java.lang.String DATA_SET = "data_set";
-    field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
-    field public static final java.lang.String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
-    field public static final java.lang.String RES_ICON = "icon";
-    field public static final java.lang.String RES_LABEL = "label";
-    field public static final java.lang.String RES_PACKAGE = "res_package";
-    field public static final java.lang.String SYNC1 = "stream_item_sync1";
-    field public static final java.lang.String SYNC2 = "stream_item_sync2";
-    field public static final java.lang.String SYNC3 = "stream_item_sync3";
-    field public static final java.lang.String SYNC4 = "stream_item_sync4";
-    field public static final java.lang.String TEXT = "text";
-    field public static final java.lang.String TIMESTAMP = "timestamp";
+  protected static abstract deprecated interface ContactsContract.StreamItemsColumns {
+    field public static final deprecated java.lang.String ACCOUNT_NAME = "account_name";
+    field public static final deprecated java.lang.String ACCOUNT_TYPE = "account_type";
+    field public static final deprecated java.lang.String COMMENTS = "comments";
+    field public static final deprecated java.lang.String CONTACT_ID = "contact_id";
+    field public static final deprecated java.lang.String CONTACT_LOOKUP_KEY = "contact_lookup";
+    field public static final deprecated java.lang.String DATA_SET = "data_set";
+    field public static final deprecated java.lang.String RAW_CONTACT_ID = "raw_contact_id";
+    field public static final deprecated java.lang.String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
+    field public static final deprecated java.lang.String RES_ICON = "icon";
+    field public static final deprecated java.lang.String RES_LABEL = "label";
+    field public static final deprecated java.lang.String RES_PACKAGE = "res_package";
+    field public static final deprecated java.lang.String SYNC1 = "stream_item_sync1";
+    field public static final deprecated java.lang.String SYNC2 = "stream_item_sync2";
+    field public static final deprecated java.lang.String SYNC3 = "stream_item_sync3";
+    field public static final deprecated java.lang.String SYNC4 = "stream_item_sync4";
+    field public static final deprecated java.lang.String TEXT = "text";
+    field public static final deprecated java.lang.String TIMESTAMP = "timestamp";
   }
 
   protected static abstract interface ContactsContract.SyncColumns implements android.provider.ContactsContract.BaseSyncColumns {
@@ -28192,6 +28284,7 @@
     method public final android.telecomm.Connection getParentConnection();
     method public final int getState();
     method public final android.telecomm.StatusHints getStatusHints();
+    method public final int getVideoState();
     method public final boolean isConferenceConnection();
     method public final boolean isRequestingRingback();
     method protected void onAbort();
@@ -28225,6 +28318,7 @@
     method public final void setRinging();
     method public final void setSignal(android.os.Bundle);
     method public final void setStatusHints(android.telecomm.StatusHints);
+    method public final void setVideoState(int);
     method public static java.lang.String stateToString(int);
   }
 
@@ -28345,27 +28439,29 @@
   }
 
   public class PhoneAccount implements android.os.Parcelable {
-    ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, int);
+    ctor public PhoneAccount(android.content.ComponentName, java.lang.String);
     method public int describeContents();
-    method public int getCapabilities();
     method public android.content.ComponentName getComponentName();
-    method public android.net.Uri getHandle();
     method public java.lang.String getId();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
-    field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
   public class PhoneAccountMetadata implements android.os.Parcelable {
-    ctor public PhoneAccountMetadata(android.telecomm.PhoneAccount, int, java.lang.String, java.lang.String);
+    ctor public PhoneAccountMetadata(android.telecomm.PhoneAccount, android.net.Uri, int, int, java.lang.String, java.lang.String, boolean);
     method public int describeContents();
     method public android.telecomm.PhoneAccount getAccount();
+    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 boolean isVideoCallingSupported();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
+    field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
@@ -28408,6 +28504,7 @@
     method public int getHandlePresentation();
     method public int getState();
     method public android.telecomm.StatusHints getStatusHints();
+    method public int getVideoState();
     method public void hold();
     method public void playDtmf(char);
     method public void postDialContinue(boolean);
@@ -28430,6 +28527,7 @@
     method public abstract void onRequestingRingback(android.telecomm.RemoteConnection, boolean);
     method public abstract void onStateChanged(android.telecomm.RemoteConnection, int);
     method public abstract void onStatusHintsChanged(android.telecomm.RemoteConnection, android.telecomm.StatusHints);
+    method public abstract void onVideoStateChanged(android.telecomm.RemoteConnection, int);
   }
 
   public abstract interface Response {
@@ -28473,9 +28571,10 @@
 
   public class TelecommManager {
     method public void clearAccounts(java.lang.String);
+    method public android.telecomm.PhoneAccount getDefaultOutgoingPhoneAccount();
     method public java.util.List<android.telecomm.PhoneAccount> getEnabledPhoneAccounts();
     method public android.telecomm.PhoneAccountMetadata getPhoneAccountMetadata(android.telecomm.PhoneAccount);
-    method public void registerPhoneAccount(android.telecomm.PhoneAccount, android.telecomm.PhoneAccountMetadata);
+    method public void registerPhoneAccount(android.telecomm.PhoneAccountMetadata);
     method public void unregisterPhoneAccount(android.telecomm.PhoneAccount);
   }
 
@@ -31194,6 +31293,64 @@
     method public android.text.style.TtsSpan.CardinalBuilder setNumber(java.lang.String);
   }
 
+  public static class TtsSpan.DateBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.DateBuilder();
+    ctor public TtsSpan.DateBuilder(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer);
+    method public android.text.style.TtsSpan.DateBuilder setDay(int);
+    method public android.text.style.TtsSpan.DateBuilder setMonth(int);
+    method public android.text.style.TtsSpan.DateBuilder setWeekday(int);
+    method public android.text.style.TtsSpan.DateBuilder setYear(int);
+  }
+
+  public static class TtsSpan.DecimalBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.DecimalBuilder();
+    ctor public TtsSpan.DecimalBuilder(double, int, int);
+    ctor public TtsSpan.DecimalBuilder(java.lang.String, java.lang.String);
+    method public android.text.style.TtsSpan.DecimalBuilder setArgumentsFromDouble(double, int, int);
+    method public android.text.style.TtsSpan.DecimalBuilder setFractionalPart(java.lang.String);
+    method public android.text.style.TtsSpan.DecimalBuilder setIntegerPart(long);
+    method public android.text.style.TtsSpan.DecimalBuilder setIntegerPart(java.lang.String);
+  }
+
+  public static class TtsSpan.DigitsBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.DigitsBuilder();
+    ctor public TtsSpan.DigitsBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.DigitsBuilder setDigits(java.lang.String);
+  }
+
+  public static class TtsSpan.FractionBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.FractionBuilder();
+    ctor public TtsSpan.FractionBuilder(long, long, long);
+    method public android.text.style.TtsSpan.FractionBuilder setDenominator(long);
+    method public android.text.style.TtsSpan.FractionBuilder setDenominator(java.lang.String);
+    method public android.text.style.TtsSpan.FractionBuilder setIntegerPart(long);
+    method public android.text.style.TtsSpan.FractionBuilder setIntegerPart(java.lang.String);
+    method public android.text.style.TtsSpan.FractionBuilder setNumerator(long);
+    method public android.text.style.TtsSpan.FractionBuilder setNumerator(java.lang.String);
+  }
+
+  public static class TtsSpan.MeasureBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.MeasureBuilder();
+    method public android.text.style.TtsSpan.MeasureBuilder setDenominator(long);
+    method public android.text.style.TtsSpan.MeasureBuilder setDenominator(java.lang.String);
+    method public android.text.style.TtsSpan.MeasureBuilder setFractionalPart(java.lang.String);
+    method public android.text.style.TtsSpan.MeasureBuilder setIntegerPart(long);
+    method public android.text.style.TtsSpan.MeasureBuilder setIntegerPart(java.lang.String);
+    method public android.text.style.TtsSpan.MeasureBuilder setNumber(long);
+    method public android.text.style.TtsSpan.MeasureBuilder setNumber(java.lang.String);
+    method public android.text.style.TtsSpan.MeasureBuilder setNumerator(long);
+    method public android.text.style.TtsSpan.MeasureBuilder setNumerator(java.lang.String);
+    method public android.text.style.TtsSpan.MeasureBuilder setUnit(java.lang.String);
+  }
+
+  public static class TtsSpan.OrdinalBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.OrdinalBuilder();
+    ctor public TtsSpan.OrdinalBuilder(long);
+    ctor public TtsSpan.OrdinalBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.OrdinalBuilder setNumber(long);
+    method public android.text.style.TtsSpan.OrdinalBuilder setNumber(java.lang.String);
+  }
+
   public static class TtsSpan.SemioticClassBuilder extends android.text.style.TtsSpan.Builder {
     ctor public TtsSpan.SemioticClassBuilder(java.lang.String);
     method public C setAnimacy(java.lang.String);
@@ -31208,6 +31365,19 @@
     method public android.text.style.TtsSpan.TextBuilder setText(java.lang.String);
   }
 
+  public static class TtsSpan.TimeBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.TimeBuilder();
+    ctor public TtsSpan.TimeBuilder(int, int);
+    method public android.text.style.TtsSpan.TimeBuilder setHours(int);
+    method public android.text.style.TtsSpan.TimeBuilder setMinutes(int);
+  }
+
+  public static class TtsSpan.VerbatimBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.VerbatimBuilder();
+    ctor public TtsSpan.VerbatimBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.VerbatimBuilder setVerbatim(java.lang.String);
+  }
+
   public class TypefaceSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public TypefaceSpan(java.lang.String);
     ctor public TypefaceSpan(android.os.Parcel);
@@ -36668,6 +36838,7 @@
     method public void setWebViewClient(android.webkit.WebViewClient);
     method public deprecated boolean showFindDialog(java.lang.String, boolean);
     method public void stopLoading();
+    method public boolean zoomBy(float);
     method public boolean zoomIn();
     method public boolean zoomOut();
     field public static final java.lang.String SCHEME_GEO = "geo:0,0?q=";
@@ -48474,6 +48645,13 @@
     method public int getWidth();
   }
 
+  public class IllformedLocaleException extends java.lang.RuntimeException {
+    ctor public IllformedLocaleException();
+    ctor public IllformedLocaleException(java.lang.String);
+    ctor public IllformedLocaleException(java.lang.String, int);
+    method public int getErrorIndex();
+  }
+
   public class InputMismatchException extends java.util.NoSuchElementException implements java.io.Serializable {
     ctor public InputMismatchException();
     ctor public InputMismatchException(java.lang.String);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 739b81c..c91c90c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -192,8 +192,10 @@
     public static final int OP_MUTE_MICROPHONE = 44;
     /** @hide */
     public static final int OP_TOAST_WINDOW = 45;
+    /** @hide Capture the device's display contents and/or audio */
+    public static final int OP_PROJECT_MEDIA = 46;
     /** @hide */
-    public static final int _NUM_OP = 46;
+    public static final int _NUM_OP = 47;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION =
@@ -263,6 +265,7 @@
             OP_GET_USAGE_STATS,
             OP_MUTE_MICROPHONE,
             OP_TOAST_WINDOW,
+            OP_PROJECT_MEDIA,
     };
 
     /**
@@ -316,6 +319,7 @@
             null,
             null,
             null,
+            null,
     };
 
     /**
@@ -367,8 +371,9 @@
             "MONITOR_LOCATION",
             "MONITOR_HIGH_POWER_LOCATION",
             "GET_USAGE_STATS",
-            "OP_MUTE_MICROPHONE",
+            "MUTE_MICROPHONE",
             "TOAST_WINDOW",
+            "PROJECT_MEDIA",
     };
 
     /**
@@ -422,6 +427,7 @@
             android.Manifest.permission.PACKAGE_USAGE_STATS,
             null, // no permission for muting/unmuting microphone
             null, // no permission for displaying toasts
+            null, // no permission for projecting media
     };
 
     /**
@@ -476,6 +482,7 @@
             null, //GET_USAGE_STATS
             UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
             UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
+            null, //PROJECT_MEDIA
     };
 
     /**
@@ -527,8 +534,9 @@
             false, //MONITOR_LOCATION
             false, //MONITOR_HIGH_POWER_LOCATION
             false, //GET_USAGE_STATS
-            false, // MUTE_MICROPHONE
-            true, // TOAST_WINDOW
+            false, //MUTE_MICROPHONE
+            true, //TOAST_WINDOW
+            false, //PROJECT_MEDIA
     };
 
     /**
@@ -581,6 +589,7 @@
             AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
     };
 
     /**
@@ -637,6 +646,7 @@
             false,
             false,
             false,
+            false,
     };
 
     private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cbfde14..2fd3443 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -72,8 +72,10 @@
 import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.MediaRouter;
+import android.media.projection.MediaProjectionManager;
 import android.media.session.MediaSessionManager;
 import android.media.tv.ITvInputManager;
+import android.media.tv.TvParentalControlManager;
 import android.media.tv.TvInputManager;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
@@ -729,6 +731,11 @@
                 return new TvInputManager(service, UserHandle.myUserId());
             }});
 
+        registerService(TV_PARENTAL_CONTROL_SERVICE, new ServiceFetcher() {
+                public Object getService(ContextImpl ctx) {
+                    return new TvParentalControlManager(ctx);
+                }});
+
         registerService(NETWORK_SCORE_SERVICE, new ServiceFetcher() {
             public Object createService(ContextImpl ctx) {
                 return new NetworkScoreManager(ctx);
@@ -752,6 +759,12 @@
                 return new PersistentDataBlockManager(
                         IPersistentDataBlockService.Stub.asInterface(b));
         }});
+
+        registerService(MEDIA_PROJECTION_SERVICE, new ServiceFetcher() {
+                public Object createService(ContextImpl ctx) {
+                    return new MediaProjectionManager(ctx);
+                }
+        });
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 61ed631..bb9032a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -212,17 +212,22 @@
     public PendingIntent fullScreenIntent;
 
     /**
-     * Text to scroll across the screen when this item is added to
-     * the status bar on large and smaller devices.
+     * Text that summarizes this notification for accessibility services.
+     *
+     * As of the L release, this text is no longer shown on screen, but it is still useful to
+     * accessibility services (where it serves as an audible announcement of the notification's
+     * appearance).
      *
      * @see #tickerView
      */
     public CharSequence tickerText;
 
     /**
-     * The view to show as the ticker in the status bar when the notification
-     * is posted.
+     * Formerly, a view showing the {@link #tickerText}.
+     *
+     * No longer displayed in the status bar as of API 21.
      */
+    @Deprecated
     public RemoteViews tickerView;
 
     /**
@@ -1576,7 +1581,6 @@
         } else {
             sb.append("null");
         }
-        // TODO(dsandler): defaults take precedence over local values, so reorder the branches below
         sb.append(" vibrate=");
         if ((this.defaults & DEFAULT_VIBRATE) != 0) {
             sb.append("default");
@@ -1620,15 +1624,35 @@
             sb.append(this.mSortKey);
         }
         if (actions != null) {
-            sb.append(" ");
+            sb.append(" actions=");
             sb.append(actions.length);
-            sb.append(" action");
-            if (actions.length > 1) sb.append("s");
+        }
+        sb.append(" vis=");
+        sb.append(visibilityToString(this.visibility));
+        if (this.publicVersion != null) {
+            sb.append(" publicVersion=");
+            sb.append(publicVersion.toString());
         }
         sb.append(")");
         return sb.toString();
     }
 
+    /**
+     * {@hide}
+     */
+    public static String visibilityToString(int vis) {
+        switch (vis) {
+            case VISIBILITY_PRIVATE:
+                return "PRIVATE";
+            case VISIBILITY_PUBLIC:
+                return "PUBLIC";
+            case VISIBILITY_SECRET:
+                return "SECRET";
+            default:
+                return "UNKNOWN(" + String.valueOf(vis) + ")";
+        }
+    }
+
     /** {@hide} */
     public void setUser(UserHandle user) {
         if (user.getIdentifier() == UserHandle.USER_ALL) {
@@ -1950,8 +1974,7 @@
         }
 
         /**
-         * Set the "ticker" text which is displayed in the status bar when the notification first
-         * arrives.
+         * Set the "ticker" text which is sent to accessibility services.
          *
          * @see Notification#tickerText
          */
@@ -1961,16 +1984,13 @@
         }
 
         /**
-         * Set the text that is displayed in the status bar when the notification first
-         * arrives, and also a RemoteViews object that may be displayed instead on some
-         * devices.
+         * Obsolete version of {@link #setTicker(CharSequence)}.
          *
-         * @see Notification#tickerText
-         * @see Notification#tickerView
          */
+        @Deprecated
         public Builder setTicker(CharSequence tickerText, RemoteViews views) {
             mTickerText = safeCharSequence(tickerText);
-            mTickerView = views;
+            mTickerView = views; // we'll save it for you anyway
             return this;
         }
 
@@ -2511,15 +2531,8 @@
         private RemoteViews makeTickerView() {
             if (mTickerView != null) {
                 return mTickerView;
-            } else {
-                if (mContentView == null) {
-                    return applyStandardTemplate(mLargeIcon == null
-                            ? R.layout.status_bar_latest_event_ticker
-                            : R.layout.status_bar_latest_event_ticker_large_icon, true);
-                } else {
-                    return null;
-                }
             }
+            return null; // tickers are not created by default anymore
         }
 
         private RemoteViews makeBigContentView() {
@@ -3223,6 +3236,10 @@
      * {@link #setShowActionsInCompactView(int...)} you can promote up to 2 actions to be displayed
      * in the standard view alongside the usual content.
      *
+     * Notifications created with MediaStyle will have their category set to
+     * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
+     * category using {@link Notification.Builder#setCategory(String) setCategory()}.
+     *
      * Finally, if you attach a {@link android.media.session.MediaSession.Token} using
      * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
      * the System UI can identify this as a notification representing an active media session
@@ -3235,9 +3252,9 @@
      *     .setSmallIcon(R.drawable.ic_stat_player)
      *     .setContentTitle(&quot;Track title&quot;)     // these three lines are optional
      *     .setContentText(&quot;Artist - Album&quot;)   // if you use
-     *     .setLargeIcon(albumArtBitmap))      // setMediaSession(token, true)
-     *     .setMediaSession(mySession, true)
-     *     .setStyle(<b>new Notification.MediaStyle()</b>)
+     *     .setLargeIcon(albumArtBitmap))      // setMediaSession(token)
+     *     .setStyle(<b>new Notification.MediaStyle()</b>
+     *         .setMediaSession(mySession))
      *     .build();
      * </pre>
      *
@@ -3279,7 +3296,9 @@
         public Notification buildStyled(Notification wip) {
             wip.contentView = makeMediaContentView();
             wip.bigContentView = makeMediaBigContentView();
-
+            if (wip.category == null) {
+                wip.category = Notification.CATEGORY_TRANSPORT;
+            }
             return wip;
         }
 
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index ff2a174..a2f3050 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -113,7 +113,6 @@
         if (targetSdkVersion < Build.VERSION_CODES.L) {
             setIcon(0);
             setTitle(R.string.time_picker_dialog_title);
-            setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_done), this);
         }
 
         final LayoutInflater inflater = LayoutInflater.from(themeContext);
@@ -122,6 +121,11 @@
 
         mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
         mTimePicker.setShowDoneButton(true);
+        // If time picker layout has no done button, add a dialog button.
+        if (!mTimePicker.isShowDoneButton()) {
+            setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_done), this);
+        }
+
         mTimePicker.setDismissCallback(new TimePicker.TimePickerDismissCallback() {
             @Override
             public void dismiss(TimePicker view, boolean isCancel, int hourOfDay, int minute) {
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index a287b3c..f8684e1 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -93,6 +93,25 @@
     public static final int GATT_FAILURE = 0x101;
 
     /**
+     * Connection paramter update - Use the connection paramters recommended by the
+     * Bluetooth SIG. This is the default value if no connection parameter update
+     * is requested.
+     */
+    public static final int GATT_CONNECTION_BALANCED = 0;
+
+    /**
+     * Connection paramter update - Request a high priority, low latency connection.
+     * An application should only request high priority connection paramters to transfer
+     * large amounts of data over LE quickly. Once the transfer is complete, the application
+     * should request {@link BluetoothGatt#GATT_CONNECTION_BALANCED} connectoin parameters
+     * to reduce energy use.
+     */
+    public static final int GATT_CONNECTION_HIGH_PRIORITY = 1;
+
+    /** Connection paramter update - Request low power, reduced data rate connection parameters. */
+    public static final int GATT_CONNECTION_LOW_POWER = 2;
+
+    /**
      * No authentication required.
      * @hide
      */
@@ -738,7 +757,7 @@
      * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
      * invoked when the connection state changes as a result of this function.
      *
-     * <p>The autoConnect paramter determines whether to actively connect to
+     * <p>The autoConnect parameter determines whether to actively connect to
      * the remote device, or rather passively scan and finalize the connection
      * when the remote device is in range/available. Generally, the first ever
      * connection to a device should be direct (autoConnect set to false) and
@@ -1287,6 +1306,38 @@
     }
 
     /**
+     * Request a connection parameter update.
+     *
+     * <p>This function will send a connection parameter update request to the
+     * remote device.
+     *
+     * @param connectionPriority Request a specific connection priority. Must be one of
+     *          {@link BluetoothGatt#GATT_CONNECTION_BALANCED},
+     *          {@link BluetoothGatt#GATT_CONNECTION_HIGH_PRIORITY}
+     *          or {@link BluetoothGatt#GATT_CONNECTION_LOW_POWER}.
+     * @throws IllegalArgumentException If the parameters are outside of their
+     *                                  specified range.
+     */
+    public boolean requestConnectionParameterUpdate(int connectionPriority) {
+        if (connectionPriority < GATT_CONNECTION_BALANCED ||
+            connectionPriority > GATT_CONNECTION_LOW_POWER) {
+            throw new IllegalArgumentException("connectionPriority not within valid range");
+        }
+
+        if (DBG) Log.d(TAG, "requestConnectionParameterUpdate() - params: " + connectionPriority);
+        if (mService == null || mClientIf == 0) return false;
+
+        try {
+            mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
      * with {@link BluetoothProfile#GATT} as argument
      *
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 6d4b9cd..533be13 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -76,6 +76,7 @@
     void endReliableWrite(in int clientIf, in String address, in boolean execute);
     void readRemoteRssi(in int clientIf, in String address);
     void configureMTU(in int clientIf, in String address, in int mtu);
+    void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority);
 
     void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
     void unregisterServer(in int serverIf);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1dd018f..b7344b7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2822,6 +2822,16 @@
     public static final String TV_INPUT_SERVICE = "tv_input";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.media.tv.TvParentalControlManager} for obtaining parental
+     * control settings and listening to their changes.
+     *
+     * @see #getSystemService
+     * @see android.media.tv.TvParentalControlManager
+     */
+    public static final String TV_PARENTAL_CONTROL_SERVICE = "tv_parental_control";
+
+    /**
      * {@link android.net.NetworkScoreManager} for managing network scoring.
      * @see #getSystemService
      * @see android.net.NetworkScoreManager
@@ -2859,6 +2869,15 @@
     public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.media.projection.MediaProjectionManager} instance for managing
+     * media projection sessions.
+     * @see #getSystemService
+     * @see android.media.projection.ProjectionManager
+     */
+    public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index de2cc67..a5c2f63 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -737,19 +737,51 @@
      * Values of events created by this sensors should not be used.
      *
      * @see #isWakeUpSensor()
-     * @hide This sensor is expected to only be used by the power manager
+     * @hide This sensor is expected to only be used by the system ui
      */
     public static final int TYPE_WAKE_GESTURE = 42;
 
     /**
      * A constant string describing a wake gesture sensor.
      *
-     * @hide This sensor is expected to only be used by the power manager
+     * @hide This sensor is expected to only be used by the system ui
      * @see #TYPE_WAKE_GESTURE
      */
     public static final String STRING_TYPE_WAKE_GESTURE = "android.sensor.wake_gesture";
 
     /**
+     * A constant describing a wake gesture sensor.
+     * <p>
+     * A sensor enabling briefly turning the screen on to enable the user to
+     * glance content on screen based on a specific motion.  The device should
+     * turn the screen off after a few moments.
+     * <p>
+     * When this sensor triggers, the device turns the screen on momentarily
+     * to allow the user to glance notifications or other content while the
+     * device remains locked in a non-interactive state (dozing). This behavior
+     * (briefly turning on the screen when this sensor triggers) might be deactivated
+     * by the user in the device settings. Changes in settings do not impact the
+     * behavior of the sensor: only whether the framework briefly turns the screen on
+     * when it triggers.
+     * <p>
+     * The actual gesture to be detected is not specified, and can be chosen by the manufacturer of
+     * the device. This sensor must be low power, as it is likely to be activated 24/7.
+     * Values of events created by this sensors should not be used.
+     *
+     * @see #isWakeUpSensor()
+     * @hide This sensor is expected to only be used by the system ui
+     */
+    public static final int TYPE_GLANCE_GESTURE = 43;
+
+    /**
+     * A constant string describing a wake gesture sensor.
+     *
+     * @hide This sensor is expected to only be used by the system ui
+     * @see #TYPE_GLANCE_GESTURE
+     */
+    public static final String STRING_TYPE_GLANCE_GESTURE = "android.sensor.glance_gesture";
+
+    /**
      * A constant describing all sensor types.
      */
     public static final int TYPE_ALL = -1;
@@ -847,6 +879,7 @@
             1, // SENSOR_TYPE_WAKE_UP_HEART_RATE_MONITOR
             1, // SENSOR_TYPE_WAKE_UP_TILT_DETECTOR
             1, // SENSOR_TYPE_WAKE_GESTURE
+            1, // SENSOR_TYPE_GLANCE_GESTURE
     };
 
     /**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index ff86120..0e4b506 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -858,6 +858,7 @@
      * then immediately dispatch this state via a partial result to
      * the application, and the rest of the metadata via later
      * partial results.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      */
     public static final Key<Integer> REQUEST_PARTIAL_RESULT_COUNT =
             new Key<Integer>("android.request.partialResultCount", int.class);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 79673b3..ce7a2a4 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,7 +16,10 @@
 
 package android.hardware.display;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
+import android.media.projection.MediaProjection;
 import android.os.Handler;
 import android.util.SparseArray;
 import android.view.Display;
@@ -188,6 +191,22 @@
      */
     public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 1 << 3;
 
+
+    /**
+     * Virtual display flag: Indicates that the display is being created for
+     * the purpose of screen sharing.  This implies
+     * VIRTUAL_DISPLAY_FLAG_PRIVATE.  Other flags are not allowed (especially
+     * not VIRTUAL_DISPLAY_FLAG_PUBLIC or PRESENTATION).
+     *
+     * Requires screen share permission for use.
+     *
+     * While a display of this type exists, the system will show some sort of
+     * notification to the user indicating that the screen is being shared.
+     *
+     * @see #createVirtualDisplay
+     */
+    public static final int VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE = 1 << 4;
+
     /** @hide */
     public DisplayManager(Context context) {
         mContext = context;
@@ -303,7 +322,7 @@
     }
 
     /**
-     * Unregisters an input device listener.
+     * Unregisters a display listener.
      *
      * @param listener The listener to unregister.
      *
@@ -425,6 +444,16 @@
 
     /**
      * Creates a virtual display.
+     *
+     * @see #createVirtualDisplay(String, int, int, int, Surface, int, VirtualDisplay.Callbacks)
+     */
+    public VirtualDisplay createVirtualDisplay(@NonNull String name,
+            int width, int height, int densityDpi, @Nullable Surface surface, int flags) {
+        return createVirtualDisplay(name, width, height, densityDpi, surface, flags, null, null);
+    }
+
+    /**
+     * Creates a virtual display.
      * <p>
      * The content of a virtual display is rendered to a {@link Surface} provided
      * by the application.
@@ -455,17 +484,28 @@
      * be rendered, or null if there is none initially.
      * @param flags A combination of virtual display flags:
      * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION},
-     * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, or {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+     * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
+     * or {@link #VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE}.
+     * @param callbacks Callbacks to call when the state of the {@link VirtualDisplay} changes
      * @return The newly created virtual display, or null if the application could
      * not create the virtual display.
      *
      * @throws SecurityException if the caller does not have permission to create
      * a virtual display with the specified flags.
      */
-    public VirtualDisplay createVirtualDisplay(String name,
-            int width, int height, int densityDpi, Surface surface, int flags) {
-        return mGlobal.createVirtualDisplay(mContext,
-                name, width, height, densityDpi, surface, flags);
+    public VirtualDisplay createVirtualDisplay(@NonNull String name,
+            int width, int height, int densityDpi, @Nullable Surface surface, int flags,
+            @Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) {
+        return createVirtualDisplay(null,
+                name, width, height, densityDpi, surface, flags, callbacks, handler);
+    }
+
+    /** @hide */
+    public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
+            @NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
+            int flags, @Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) {
+        return mGlobal.createVirtualDisplay(mContext, projection,
+                name, width, height, densityDpi, surface, flags, callbacks, handler);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a8d55e8..f2426e5 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.hardware.display.DisplayManager.DisplayListener;
+import android.media.projection.MediaProjection;
+import android.media.projection.IMediaProjection;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -368,8 +370,9 @@
         }
     }
 
-    public VirtualDisplay createVirtualDisplay(Context context, String name,
-            int width, int height, int densityDpi, Surface surface, int flags) {
+    public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
+            String name, int width, int height, int densityDpi, Surface surface, int flags,
+            VirtualDisplay.Callbacks callbacks, Handler handler) {
         if (TextUtils.isEmpty(name)) {
             throw new IllegalArgumentException("name must be non-null and non-empty");
         }
@@ -378,11 +381,12 @@
                     + "greater than 0");
         }
 
-        Binder token = new Binder();
+        VirtualDisplayCallbacks callbackWrapper = new VirtualDisplayCallbacks(callbacks, handler);
+        IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
         int displayId;
         try {
-            displayId = mDm.createVirtualDisplay(token, context.getPackageName(),
-                    name, width, height, densityDpi, surface, flags);
+            displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
+                    context.getPackageName(), name, width, height, densityDpi, surface, flags);
         } catch (RemoteException ex) {
             Log.e(TAG, "Could not create virtual display: " + name, ex);
             return null;
@@ -396,15 +400,15 @@
             Log.wtf(TAG, "Could not obtain display info for newly created "
                     + "virtual display: " + name);
             try {
-                mDm.releaseVirtualDisplay(token);
+                mDm.releaseVirtualDisplay(callbackWrapper);
             } catch (RemoteException ex) {
             }
             return null;
         }
-        return new VirtualDisplay(this, display, token, surface);
+        return new VirtualDisplay(this, display, callbackWrapper, surface);
     }
 
-    public void setVirtualDisplaySurface(IBinder token, Surface surface) {
+    public void setVirtualDisplaySurface(IVirtualDisplayCallbacks token, Surface surface) {
         try {
             mDm.setVirtualDisplaySurface(token, surface);
         } catch (RemoteException ex) {
@@ -412,7 +416,7 @@
         }
     }
 
-    public void releaseVirtualDisplay(IBinder token) {
+    public void releaseVirtualDisplay(IVirtualDisplayCallbacks token) {
         try {
             mDm.releaseVirtualDisplay(token);
         } catch (RemoteException ex) {
@@ -462,4 +466,59 @@
             }
         }
     }
+
+    private final static class VirtualDisplayCallbacks extends IVirtualDisplayCallbacks.Stub {
+        private VirtualDisplayCallbacksDelegate mDelegate;
+
+        public VirtualDisplayCallbacks(VirtualDisplay.Callbacks callbacks, Handler handler) {
+            mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler);
+        }
+
+        @Override // Binder call
+        public void onDisplayPaused() {
+            mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED);
+        }
+
+        @Override // Binder call
+        public void onDisplayResumed() {
+            mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED);
+        }
+
+        @Override // Binder call
+        public void onDisplayStopped() {
+            mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED);
+        }
+    }
+
+    private final static class VirtualDisplayCallbacksDelegate extends Handler {
+        public static final int MSG_DISPLAY_PAUSED = 0;
+        public static final int MSG_DISPLAY_RESUMED = 1;
+        public static final int MSG_DISPLAY_STOPPED = 2;
+
+        private final VirtualDisplay.Callbacks mCallbacks;
+
+        public VirtualDisplayCallbacksDelegate(VirtualDisplay.Callbacks callbacks,
+                Handler handler) {
+            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
+            mCallbacks = callbacks;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (mCallbacks == null) {
+                return;
+            }
+            switch (msg.what) {
+                case MSG_DISPLAY_PAUSED:
+                    mCallbacks.onDisplayPaused();
+                    break;
+                case MSG_DISPLAY_RESUMED:
+                    mCallbacks.onDisplayResumed();
+                    break;
+                case MSG_DISPLAY_STOPPED:
+                    mCallbacks.onDisplayStopped();
+                    break;
+            }
+        }
+    }
 }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 23c58c8..44ffbc4 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -17,8 +17,10 @@
 package android.hardware.display;
 
 import android.hardware.display.IDisplayManagerCallback;
+import android.hardware.display.IVirtualDisplayCallbacks;
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
+import android.media.projection.IMediaProjection;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
@@ -57,14 +59,15 @@
     // No permissions required.
     WifiDisplayStatus getWifiDisplayStatus();
 
-    // Requires CAPTURE_VIDEO_OUTPUT or CAPTURE_SECURE_VIDEO_OUTPUT for certain
-    // combinations of flags.
-    int createVirtualDisplay(IBinder token, String packageName,
-            String name, int width, int height, int densityDpi, in Surface surface, int flags);
+    // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
+    // MediaProjection token for certain combinations of flags.
+    int createVirtualDisplay(in IVirtualDisplayCallbacks callbacks,
+            in IMediaProjection projectionToken, String packageName, String name,
+            int width, int height, int densityDpi, in Surface surface, int flags);
 
     // No permissions required but must be same Uid as the creator.
-    void setVirtualDisplaySurface(in IBinder token, in Surface surface);
+    void setVirtualDisplaySurface(in IVirtualDisplayCallbacks token, in Surface surface);
 
     // No permissions required but must be same Uid as the creator.
-    void releaseVirtualDisplay(in IBinder token);
+    void releaseVirtualDisplay(in IVirtualDisplayCallbacks token);
 }
diff --git a/core/java/android/hardware/display/IVirtualDisplayCallbacks.aidl b/core/java/android/hardware/display/IVirtualDisplayCallbacks.aidl
new file mode 100644
index 0000000..a1cdc01
--- /dev/null
+++ b/core/java/android/hardware/display/IVirtualDisplayCallbacks.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.display;
+
+/** @hide */
+oneway interface IVirtualDisplayCallbacks {
+    /**
+     * Called when the virtual display video projection has been
+     * paused by the system or when the surface has been detached
+     * by the application by calling setSurface(null).
+     * The surface will not receive any more buffers while paused.
+     */
+    void onDisplayPaused();
+
+    /**
+     * Called when the virtual display video projection has been
+     * resumed after having been paused.
+     */
+    void onDisplayResumed();
+
+    /**
+     * Called when the virtual display video projection has been
+     * stopped by the system.  It will no longer receive frames
+     * and it will never be resumed.  It is still the responsibility
+     * of the application to release() the virtual display.
+     */
+    void onDisplayStopped();
+}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 691d6a0..df6116b 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -35,11 +35,11 @@
 public final class VirtualDisplay {
     private final DisplayManagerGlobal mGlobal;
     private final Display mDisplay;
-    private IBinder mToken;
+    private IVirtualDisplayCallbacks mToken;
     private Surface mSurface;
 
-    VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token,
-            Surface surface) {
+    VirtualDisplay(DisplayManagerGlobal global, Display display,
+            IVirtualDisplayCallbacks token, Surface surface) {
         mGlobal = global;
         mDisplay = display;
         mToken = token;
@@ -98,4 +98,31 @@
         return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
                 + ", surface=" + mSurface + "}";
     }
+
+    /**
+     * Interface for receiving information about a {@link VirtualDisplay}'s state changes.
+     */
+    public static abstract class Callbacks {
+        /**
+         * Called when the virtual display video projection has been
+         * paused by the system or when the surface has been detached
+         * by the application by calling setSurface(null).
+         * The surface will not receive any more buffers while paused.
+         */
+         public void onDisplayPaused() { }
+
+        /**
+         * Called when the virtual display video projection has been
+         * resumed after having been paused.
+         */
+         public void onDisplayResumed() { }
+
+        /**
+         * Called when the virtual display video projection has been
+         * stopped by the system.  It will no longer receive frames
+         * and it will never be resumed.  It is still the responsibility
+         * of the application to release() the virtual display.
+         */
+        public void onDisplayStopped() { }
+    }
 }
diff --git a/core/java/android/hardware/location/ActivityChangedEvent.java b/core/java/android/hardware/location/ActivityChangedEvent.java
index 0a89207..16cfe6e 100644
--- a/core/java/android/hardware/location/ActivityChangedEvent.java
+++ b/core/java/android/hardware/location/ActivityChangedEvent.java
@@ -76,4 +76,17 @@
         parcel.writeInt(activityRecognitionEventArray.length);
         parcel.writeTypedArray(activityRecognitionEventArray, flags);
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:");
+
+        for (ActivityRecognitionEvent event : mActivityRecognitionEvents) {
+            builder.append("\n    ");
+            builder.append(event.toString());
+        }
+        builder.append("\n]");
+
+        return builder.toString();
+    }
 }
diff --git a/core/java/android/hardware/location/ActivityRecognitionEvent.java b/core/java/android/hardware/location/ActivityRecognitionEvent.java
index 5aeb899..190030a 100644
--- a/core/java/android/hardware/location/ActivityRecognitionEvent.java
+++ b/core/java/android/hardware/location/ActivityRecognitionEvent.java
@@ -75,4 +75,13 @@
         parcel.writeInt(mEventType);
         parcel.writeLong(mTimestampNs);
     }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "Activity='%s', EventType=%s, TimestampNs=%s",
+                mActivity,
+                mEventType,
+                mTimestampNs);
+    }
 }
diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java
index a4ce4ac..5d3953a 100644
--- a/core/java/android/hardware/location/ActivityRecognitionHardware.java
+++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java
@@ -134,8 +134,8 @@
      * Called by the Activity-Recognition HAL.
      */
     private void onActivityChanged(Event[] events) {
-        int size = mSinks.beginBroadcast();
-        if (size == 0 || events == null || events.length == 0) {
+        if (events == null || events.length == 0) {
+            Log.d(TAG, "No events to broadcast for onActivityChanged.");
             return;
         }
 
@@ -151,6 +151,7 @@
         ActivityChangedEvent activityChangedEvent =
                 new ActivityChangedEvent(activityRecognitionEventArray);
 
+        int size = mSinks.beginBroadcast();
         for (int i = 0; i < size; ++i) {
             IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
             try {
@@ -181,8 +182,8 @@
             return INVALID_ACTIVITY_TYPE;
         }
 
-        int supporteActivitiesLength = mSupportedActivities.length;
-        for (int i = 0; i < supporteActivitiesLength; ++i) {
+        int supportedActivitiesLength = mSupportedActivities.length;
+        for (int i = 0; i < supportedActivitiesLength; ++i) {
             if (activity.equals(mSupportedActivities[i])) {
                 return i;
             }
@@ -198,7 +199,7 @@
         mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
     }
 
-    private static String[] fetchSupportedActivities() {
+    private String[] fetchSupportedActivities() {
         String[] supportedActivities = nativeGetSupportedActivities();
         if (supportedActivities != null) {
             return supportedActivities;
@@ -211,14 +212,15 @@
     static { nativeClassInit(); }
 
     private static native void nativeClassInit();
-    private static native void nativeInitialize();
-    private static native void nativeRelease();
     private static native boolean nativeIsSupported();
-    private static native String[] nativeGetSupportedActivities();
-    private static native int nativeEnableActivityEvent(
+
+    private native void nativeInitialize();
+    private native void nativeRelease();
+    private native String[] nativeGetSupportedActivities();
+    private native int nativeEnableActivityEvent(
             int activityType,
             int eventType,
             long reportLatenceNs);
-    private static native int nativeDisableActivityEvent(int activityType, int eventType);
-    private static native int nativeFlush();
+    private native int nativeDisableActivityEvent(int activityType, int eventType);
+    private native int nativeFlush();
 }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index bfe90e6..829d459 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1721,16 +1721,28 @@
          * Querying for social stream data requires android.permission.READ_SOCIAL_STREAM
          * permission.
          * </p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final class StreamItems implements StreamItemsColumns {
             /**
              * no public constructor since this is a utility class
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             private StreamItems() {}
 
             /**
              * The directory twig for this sub-table
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             public static final String CONTENT_DIRECTORY = "stream_items";
         }
 
@@ -2830,17 +2842,29 @@
          * inserting or updating social stream items requires android.permission.WRITE_SOCIAL_STREAM
          * permission.
          * </p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final class StreamItems implements BaseColumns, StreamItemsColumns {
             /**
              * No public constructor since this is a utility class
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             private StreamItems() {
             }
 
             /**
              * The directory twig for this sub-table
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             public static final String CONTENT_DIRECTORY = "stream_items";
         }
 
@@ -3255,18 +3279,30 @@
      * </pre>
      * </dd>
      * </dl>
+     *
+     * @deprecated - Do not use. This will not be supported in the future. In the future,
+     * cursors returned from related queries will be empty.
      */
+    @Deprecated
     public static final class StreamItems implements BaseColumns, StreamItemsColumns {
         /**
          * This utility class cannot be instantiated
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         private StreamItems() {
         }
 
         /**
          * The content:// style URI for this table, which handles social network stream
          * updates for the user's contacts.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "stream_items");
 
         /**
@@ -3281,31 +3317,51 @@
          * When using this URI, the stream item ID for the photo(s) must be identified
          * in the {@link ContentValues} passed in.
          * </p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final Uri CONTENT_PHOTO_URI = Uri.withAppendedPath(CONTENT_URI, "photo");
 
         /**
          * This URI allows the caller to query for the maximum number of stream items
          * that will be stored under any single raw contact.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final Uri CONTENT_LIMIT_URI =
                 Uri.withAppendedPath(AUTHORITY_URI, "stream_items_limit");
 
         /**
          * The MIME type of a directory of stream items.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
 
         /**
          * The MIME type of a single stream item.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item";
 
         /**
          * Queries to {@link ContactsContract.StreamItems#CONTENT_LIMIT_URI} will
          * contain this column, with the value indicating the maximum number of
          * stream items that will be stored under any single raw contact.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String MAX_ITEMS = "max_items";
 
         /**
@@ -3321,28 +3377,48 @@
          * requires android.permission.READ_SOCIAL_STREAM permission, and inserting or updating
          * social stream photos requires android.permission.WRITE_SOCIAL_STREAM permission.
          * </p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final class StreamItemPhotos
                 implements BaseColumns, StreamItemPhotosColumns {
             /**
              * No public constructor since this is a utility class
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             private StreamItemPhotos() {
             }
 
             /**
              * The directory twig for this sub-table
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             public static final String CONTENT_DIRECTORY = "photo";
 
             /**
              * The MIME type of a directory of stream item photos.
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
 
             /**
              * The MIME type of a single stream item photo.
+             *
+             * @deprecated - Do not use. This will not be supported in the future. In the future,
+             * cursors returned from related queries will be empty.
              */
+            @Deprecated
             public static final String CONTENT_ITEM_TYPE
                     = "vnd.android.cursor.item/stream_item_photo";
         }
@@ -3352,7 +3428,10 @@
      * Columns in the StreamItems table.
      *
      * @see ContactsContract.StreamItems
+     * @deprecated - Do not use. This will not be supported in the future. In the future,
+     * cursors returned from related queries will be empty.
      */
+    @Deprecated
     protected interface StreamItemsColumns {
         /**
          * A reference to the {@link android.provider.ContactsContract.Contacts#_ID}
@@ -3360,7 +3439,11 @@
          *
          * <p>Type: INTEGER</p>
          * <p>read-only</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String CONTACT_ID = "contact_id";
 
         /**
@@ -3369,14 +3452,22 @@
          *
          * <p>Type: TEXT</p>
          * <p>read-only</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String CONTACT_LOOKUP_KEY = "contact_lookup";
 
         /**
          * A reference to the {@link RawContacts#_ID}
          * that this stream item belongs to.
          * <p>Type: INTEGER</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String RAW_CONTACT_ID = "raw_contact_id";
 
         /**
@@ -3384,7 +3475,11 @@
          * this stream item. This value is only designed for use when building
          * user interfaces, and should not be used to infer the owner.
          * <P>Type: TEXT</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String RES_PACKAGE = "res_package";
 
         /**
@@ -3393,7 +3488,11 @@
          *
          * <p>Type: TEXT</p>
          * <p>read-only</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String ACCOUNT_TYPE = "account_type";
 
         /**
@@ -3402,7 +3501,11 @@
          *
          * <p>Type: TEXT</p>
          * <p>read-only</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String ACCOUNT_NAME = "account_name";
 
         /**
@@ -3413,7 +3516,11 @@
          *
          * <P>Type: TEXT</P>
          * <p>read-only</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String DATA_SET = "data_set";
 
         /**
@@ -3422,7 +3529,11 @@
          *
          * <P>Type: TEXT</P>
          * <p>read-only</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
 
         /**
@@ -3430,7 +3541,11 @@
          * This resource should be scoped by the {@link #RES_PACKAGE}. As this can only reference
          * drawables, the "@drawable/" prefix must be omitted.
          * <P>Type: TEXT</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String RES_ICON = "icon";
 
         /**
@@ -3438,7 +3553,11 @@
          * Talk". This resource should be scoped by the {@link #RES_PACKAGE}. As this can only
          * reference strings, the "@string/" prefix must be omitted.
          * <p>Type: TEXT</p>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String RES_LABEL = "label";
 
         /**
@@ -3455,14 +3574,22 @@
          * is unspecified, but it should not break tags.
          * </P>
          * <P>Type: TEXT</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String TEXT = "text";
 
         /**
          * The absolute time (milliseconds since epoch) when this stream item was
          * inserted/updated.
          * <P>Type: NUMBER</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String TIMESTAMP = "timestamp";
 
         /**
@@ -3480,16 +3607,44 @@
          * is unspecified, but it should not break tags.
          * </P>
          * <P>Type: TEXT</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String COMMENTS = "comments";
 
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC1 = "stream_item_sync1";
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC2 = "stream_item_sync2";
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC3 = "stream_item_sync3";
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC4 = "stream_item_sync4";
     }
 
@@ -3662,11 +3817,19 @@
      * <pre>
      * </dd>
      * </dl>
+     *
+     * @deprecated - Do not use. This will not be supported in the future. In the future,
+     * cursors returned from related queries will be empty.
      */
+    @Deprecated
     public static final class StreamItemPhotos implements BaseColumns, StreamItemPhotosColumns {
         /**
          * No public constructor since this is a utility class
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         private StreamItemPhotos() {
         }
 
@@ -3681,7 +3844,11 @@
          * as an asset file.
          * </p>
          * <P>Type: BLOB</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String PHOTO = "photo";
     }
 
@@ -3689,42 +3856,85 @@
      * Columns in the StreamItemPhotos table.
      *
      * @see ContactsContract.StreamItemPhotos
+     * @deprecated - Do not use. This will not be supported in the future. In the future,
+     * cursors returned from related queries will be empty.
      */
+    @Deprecated
     protected interface StreamItemPhotosColumns {
         /**
          * A reference to the {@link StreamItems#_ID} this photo is associated with.
          * <P>Type: NUMBER</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String STREAM_ITEM_ID = "stream_item_id";
 
         /**
          * An integer to use for sort order for photos in the stream item.  If not
          * specified, the {@link StreamItemPhotos#_ID} will be used for sorting.
          * <P>Type: NUMBER</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String SORT_INDEX = "sort_index";
 
         /**
          * Photo file ID for the photo.
          * See {@link ContactsContract.DisplayPhoto}.
          * <P>Type: NUMBER</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String PHOTO_FILE_ID = "photo_file_id";
 
         /**
          * URI for retrieving the photo content, automatically populated.  Callers
          * may retrieve the photo content by opening this URI as an asset file.
          * <P>Type: TEXT</P>
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
          */
+        @Deprecated
         public static final String PHOTO_URI = "photo_uri";
 
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC1 = "stream_item_photo_sync1";
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC2 = "stream_item_photo_sync2";
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC3 = "stream_item_photo_sync3";
-        /** Generic column for use by sync adapters. */
+        /**
+         * Generic column for use by sync adapters.
+         *
+         * @deprecated - Do not use. This will not be supported in the future. In the future,
+         * cursors returned from related queries will be empty.
+         */
+        @Deprecated
         public static final String SYNC4 = "stream_item_photo_sync4";
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bc069ca..d6ec30b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3627,6 +3627,20 @@
          */
         public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
 
+
+        /**
+         * Whether the TV parental control is enabled.
+         * @hide
+         */
+        public static final String TV_PARENTAL_CONTROL_ENABLED = "tv_parental_control_enabled";
+
+        /**
+         * List of TV content ratings blocked by the user. (comma-delimited)
+         * @hide
+         */
+        public static final String TV_PARENTAL_CONTROL_BLOCKED_RATINGS =
+                "tv_parental_control_blocked_ratings";
+
         /**
          * Settings classname to launch when Settings is clicked from All
          * Applications.  Needed because of user testing between the old
@@ -5154,17 +5168,6 @@
        public static final String HDMI_SYSTEM_AUDIO_ENABLED = "hdmi_system_audio_enabled";
 
        /**
-        * Output of the audio to be used for system audio mode, as defined in AudioSystem.java.
-        * <ul>
-        * <li>DEVICE_OUT_SPDIF</li>
-        * <li>DEVICE_OUT_HDMI_ARC</li>
-        * <li>DEVICE_OUT_LINE</li>
-        * </ul>
-        * @hide
-        */
-       public static final String HDMI_SYSTEM_AUDIO_OUTPUT = "hdmi_system_audio_output";
-
-       /**
         * Whether TV will automatically turn on upon reception of the CEC command
         * &lt;Text View On&gt; or &lt;Image View On&gt;. (0 = false, 1 = true)
         * @hide
diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
index 06e776d..52db223 100644
--- a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
+++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.service.persistentdata;
 
 import android.os.ParcelFileDescriptor;
@@ -12,8 +28,10 @@
  */
 interface IPersistentDataBlockService {
     int write(in byte[] data);
-    int read(out byte[] data);
+    byte[] read();
+    void wipe();
     int getDataBlockSize();
+    long getMaximumDataBlockSize();
 
     void setOemUnlockEnabled(boolean enabled);
     boolean getOemUnlockEnabled();
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index afcf717..42a2e57 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.service.persistentdata;
 
 import android.os.RemoteException;
@@ -7,14 +23,16 @@
  * Interface for reading and writing data blocks to a persistent partition.
  *
  * Allows writing one block at a time. Namely, each time
- * {@link android.service.persistentdata.PersistentDataBlockManager}.write(byte[] data)
+ * {@link PersistentDataBlockManager#write(byte[])}
  * is called, it will overwite the data that was previously written on the block.
  *
  * Clients can query the size of the currently written block via
- * {@link android.service.persistentdata.PersistentDataBlockManager}.getTotalDataSize().
+ * {@link PersistentDataBlockManager#getDataBlockSize()}.
  *
- * Clients can any number of bytes from the currently written block up to its total size by invoking
- * {@link android.service.persistentdata.PersistentDataBlockManager}.read(byte[] data).
+ * Clients can query the maximum size for a block via
+ *
+ * Clients can read the currently written block by invoking
+ * {@link PersistentDataBlockManager#read()}.
  *
  * @hide
  */
@@ -30,41 +48,69 @@
      * Writes {@code data} to the persistent partition. Previously written data
      * will be overwritten. This data will persist across factory resets.
      *
+     * Returns the number of bytes written or -1 on error. If the block is too big
+     * to fit on the partition, returns -MAX_BLOCK_SIZE.
+     *
      * @param data the data to write
      */
-    public void write(byte[] data) {
+    public int write(byte[] data) {
         try {
-            sService.write(data);
+            return sService.write(data);
         } catch (RemoteException e) {
             onError("writing data");
-        }
-    }
-
-    /**
-     * Tries to read {@code data.length} bytes into {@code data}. Call {@code getDataBlockSize()}
-     * to determine the total size of the block currently residing in the persistent partition.
-     *
-     * @param data the buffer in which to read the data
-     * @return the actual number of bytes read
-     */
-    public int read(byte[] data) {
-        try {
-            return sService.read(data);
-        } catch (RemoteException e) {
-            onError("reading data");
             return -1;
         }
     }
 
     /**
+     * Returns the data block stored on the persistent partition.
+     */
+    public byte[] read() {
+        try {
+            return sService.read();
+        } catch (RemoteException e) {
+            onError("reading data");
+            return null;
+        }
+    }
+
+    /**
      * Retrieves the size of the block currently written to the persistent partition.
+     *
+     * Return -1 on error.
      */
     public int getDataBlockSize() {
         try {
             return sService.getDataBlockSize();
         } catch (RemoteException e) {
             onError("getting data block size");
-            return 0;
+            return -1;
+        }
+    }
+
+    /**
+     * Retrieves the maximum size allowed for a data block.
+     *
+     * Returns -1 on error.
+     */
+    public long getMaximumDataBlockSize() {
+        try {
+            return sService.getMaximumDataBlockSize();
+        } catch (RemoteException e) {
+            onError("getting maximum data block size");
+            return -1;
+        }
+    }
+
+    /**
+     * Zeroes the previously written block in its entirety. Calling this method
+     * will erase all data written to the persistent data partition.
+     */
+    public void wipe() {
+        try {
+            sService.wipe();
+        } catch (RemoteException e) {
+            onError("wiping persistent partition");
         }
     }
 
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index 3ab3b31..04159af 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -16,6 +16,9 @@
 
 package android.text.style;
 
+import java.text.NumberFormat;
+import java.util.Locale;
+
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.text.ParcelableSpan;
@@ -228,7 +231,8 @@
 
     /**
      * Argument used to specify the integer part of a decimal or fraction. The
-     * value can be a string of digits of any size optionally prefixed with a - or +.
+     * value can be a string of digits of any size optionally prefixed with
+     * a - or +.
      * Can be used with {@link #TYPE_DECIMAL} and {@link #TYPE_FRACTION}.
      */
     public static final String ARG_INTEGER_PART = "android.arg.integer_part";
@@ -308,9 +312,10 @@
     /**
      * Argument used to specify the month of a date. The value should be
      * provided as an integer and can be any of {@link #MONTH_JANUARY},
-     * {@link #MONTH_FEBRUARY},  {@link #MONTH_MARCH}, {@link #MONTH_APRIL}, {@link #MONTH_MAY},
-     * {@link #MONTH_JUNE}, {@link #MONTH_JULY}, {@link #MONTH_AUGUST}, {@link #MONTH_SEPTEMBER},
-     * {@link #MONTH_OCTOBER}, {@link #MONTH_NOVEMBER} and {@link #MONTH_DECEMBER}.
+     * {@link #MONTH_FEBRUARY},  {@link #MONTH_MARCH}, {@link #MONTH_APRIL},
+     * {@link #MONTH_MAY}, {@link #MONTH_JUNE}, {@link #MONTH_JULY},
+     * {@link #MONTH_AUGUST}, {@link #MONTH_SEPTEMBER}, {@link #MONTH_OCTOBER},
+     * {@link #MONTH_NOVEMBER} and {@link #MONTH_DECEMBER}.
      * Can be used with {@link #TYPE_DATE}.
      */
     public static final String ARG_MONTH = "android.arg.month";
@@ -344,7 +349,8 @@
 
     /**
      * Argument used to specify the main number part of a telephone number. Can
-     * be a string of digits.
+     * be a string of digits where the different parts of the telephone number
+     * can be separated with a space, '-', '/' or '.'.
      * Can be used with {@link #TYPE_TELEPHONE}.
      */
     public static final String ARG_NUMBER_PART = "android.arg.number_part";
@@ -473,8 +479,8 @@
      * this builder like {@link TtsSpan.TextBuilder} and
      * {@link TtsSpan.CardinalBuilder} are likely more useful.
      *
-     * This class uses generics so methods from this class can return instances of
-     * its child classes, resulting in a fluent API (CRTP pattern).
+     * This class uses generics so methods from this class can return instances
+     * of its child classes, resulting in a fluent API (CRTP pattern).
      */
     public static abstract class Builder<C extends Builder<C>> {
         // Holds the type of this class.
@@ -597,7 +603,7 @@
     public static class TextBuilder extends SemioticClassBuilder<TextBuilder> {
 
         /**
-         * Creates a TtsSpan of type {@link TtsSpan#TYPE_TEXT}.
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_TEXT}.
          */
         public TextBuilder() {
             super(TtsSpan.TYPE_TEXT);
@@ -628,10 +634,12 @@
     /**
      * A builder for TtsSpans of type {@link TtsSpan #TYPE_CARDINAL}.
      */
-    public static class CardinalBuilder extends SemioticClassBuilder<CardinalBuilder> {
+    public static class CardinalBuilder
+            extends SemioticClassBuilder<CardinalBuilder> {
 
         /**
-         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL}.
+         * Creates a builder for a TtsSpan of type
+         * {@link TtsSpan#TYPE_CARDINAL}.
          */
         public CardinalBuilder() {
             super(TtsSpan.TYPE_CARDINAL);
@@ -679,4 +687,570 @@
             return setStringArgument(TtsSpan.ARG_NUMBER, number);
         }
     }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan#TYPE_ORDINAL}.
+     */
+    public static class OrdinalBuilder
+            extends SemioticClassBuilder<OrdinalBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_ORDINAL}.
+         */
+        public OrdinalBuilder() {
+            super(TtsSpan.TYPE_ORDINAL);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_ORDINAL} and sets the
+         * {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number The ordinal number to synthesize.
+         * @see #setNumber(long)
+         */
+        public OrdinalBuilder(long number) {
+            this();
+            setNumber(number);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_ORDINAL} and sets the
+         * {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number The number to synthesize.
+         * @see #setNumber(String)
+         */
+        public OrdinalBuilder(String number) {
+            this();
+            setNumber(number);
+        }
+
+        /**
+         * Convenience method that converts the number to a String and sets it
+         * to the value for {@link TtsSpan#ARG_NUMBER}.
+         * @param number The ordinal number that will be synthesized.
+         * @return This instance.
+         */
+        public OrdinalBuilder setNumber(long number) {
+            return setNumber(String.valueOf(number));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public OrdinalBuilder setNumber(String number) {
+            return setStringArgument(TtsSpan.ARG_NUMBER, number);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan#TYPE_DECIMAL}.
+     */
+    public static class DecimalBuilder
+            extends SemioticClassBuilder<DecimalBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_DECIMAL}.
+         */
+        public DecimalBuilder() {
+            super(TtsSpan.TYPE_DECIMAL);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_DECIMAL} and sets the
+         * {@link TtsSpan#ARG_INTEGER_PART} and
+         * {@link TtsSpan#ARG_FRACTIONAL_PART} arguments.
+         * @see {@link #setArgumentsFromDouble(double, int, int)
+         */
+        public DecimalBuilder(double number,
+                              int minimumFractionDigits,
+                              int maximumFractionDigits) {
+            this();
+            setArgumentsFromDouble(number,
+                                   minimumFractionDigits,
+                                   maximumFractionDigits);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_DECIMAL} and sets the
+         * {@link TtsSpan#ARG_INTEGER_PART} and
+         * {@link TtsSpan#ARG_FRACTIONAL_PART} arguments.
+         */
+        public DecimalBuilder(String integerPart, String fractionalPart) {
+            this();
+            setIntegerPart(integerPart);
+            setFractionalPart(fractionalPart);
+        }
+
+        /**
+         * Convenience method takes a double and a maximum number of fractional
+         * digits, it sets the {@link TtsSpan#ARG_INTEGER_PART} and
+         * {@link TtsSpan#ARG_FRACTIONAL_PART} arguments.
+         * @param number The number to be synthesized.
+         * @param minimumFractionDigits The minimum number of fraction digits
+         *     that are pronounced.
+         * @param maximumFractionDigits The maximum number of fraction digits
+         *     that are pronounced. If maximumFractionDigits <
+         *     minimumFractionDigits then minimumFractionDigits will be assumed
+         *     to be equal to maximumFractionDigits.
+         * @return This instance.
+         */
+        public DecimalBuilder setArgumentsFromDouble(
+                double number,
+                int minimumFractionDigits,
+                int maximumFractionDigits) {
+            // Format double.
+            NumberFormat formatter = NumberFormat.getInstance(Locale.US);
+            formatter.setMinimumFractionDigits(maximumFractionDigits);
+            formatter.setMaximumFractionDigits(maximumFractionDigits);
+            formatter.setGroupingUsed(false);
+            String str = formatter.format(number);
+
+            // Split at decimal point.
+            int i = str.indexOf('.');
+            if (i >= 0) {
+                setIntegerPart(str.substring(0, i));
+                setFractionalPart(str.substring(i + 1));
+            } else {
+                setIntegerPart(str);
+            }
+            return this;
+        }
+
+        /**
+         * Convenience method that converts the number to a String and sets it
+         * to the value for {@link TtsSpan#ARG_INTEGER_PART}.
+         * @param integerPart The integer part of the decimal.
+         * @return This instance.
+         */
+        public DecimalBuilder setIntegerPart(long integerPart) {
+            return setIntegerPart(String.valueOf(integerPart));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_INTEGER_PART} argument.
+         * @param integerPart A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public DecimalBuilder setIntegerPart(String integerPart) {
+            return setStringArgument(TtsSpan.ARG_INTEGER_PART, integerPart);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_FRACTIONAL_PART} argument.
+         * @param fractionalPart A non-empty string of digits.
+         * @return This instance.
+         */
+        public DecimalBuilder setFractionalPart(String fractionalPart) {
+            return setStringArgument(TtsSpan.ARG_FRACTIONAL_PART,
+                                     fractionalPart);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan#TYPE_FRACTION}.
+     */
+    public static class FractionBuilder
+            extends SemioticClassBuilder<FractionBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type
+         * {@link TtsSpan#TYPE_FRACTION}.
+         */
+        public FractionBuilder() {
+            super(TtsSpan.TYPE_FRACTION);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_FRACTION} and sets the
+         * {@link TtsSpan#ARG_INTEGER_PART}, {@link TtsSpan#ARG_NUMERATOR}, and
+         * {@link TtsSpan#ARG_DENOMINATOR} arguments.
+         */
+        public FractionBuilder(long integerPart,
+                               long numerator,
+                               long denominator) {
+            this();
+            setIntegerPart(integerPart);
+            setNumerator(numerator);
+            setDenominator(denominator);
+        }
+
+
+        /**
+         * Convenience method that converts the integer to a String and sets the
+         * argument {@link TtsSpan#ARG_NUMBER}.
+         * @param integerPart The integer part.
+         * @return This instance.
+         */
+        public FractionBuilder setIntegerPart(long integerPart) {
+            return setIntegerPart(String.valueOf(integerPart));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_INTEGER_PART} argument.
+         * @param integerPart A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public FractionBuilder setIntegerPart(String integerPart) {
+            return setStringArgument(TtsSpan.ARG_INTEGER_PART, integerPart);
+        }
+
+        /**
+         * Convenience method that converts the numerator to a String and sets
+         * the argument {@link TtsSpan#ARG_NUMERATOR}.
+         * @param numerator The numerator.
+         * @return This instance.
+         */
+        public FractionBuilder setNumerator(long numerator) {
+            return setNumerator(String.valueOf(numerator));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_NUMERATOR} argument.
+         * @param numerator A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public FractionBuilder setNumerator(String numerator) {
+            return setStringArgument(TtsSpan.ARG_NUMERATOR, numerator);
+        }
+
+        /**
+         * Convenience method that converts the denominator to a String and sets
+         * the argument {@link TtsSpan#ARG_DENOMINATOR}.
+         * @param denominator The denominator.
+         * @return This instance.
+         */
+        public FractionBuilder setDenominator(long denominator) {
+            return setDenominator(String.valueOf(denominator));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_DENOMINATOR} argument.
+         * @param denominator A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public FractionBuilder setDenominator(String denominator) {
+            return setStringArgument(TtsSpan.ARG_DENOMINATOR, denominator);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_MEASURE}.
+     */
+    public static class MeasureBuilder
+            extends SemioticClassBuilder<MeasureBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_MEASURE}.
+         */
+        public MeasureBuilder() {
+            super(TtsSpan.TYPE_MEASURE);
+        }
+
+        /**
+         * Convenience method that converts the number to a String and set it to
+         * the value for {@link TtsSpan#ARG_NUMBER}.
+         * @param number The amount of the measure.
+         * @return This instance.
+         */
+        public MeasureBuilder setNumber(long number) {
+            return setNumber(String.valueOf(number));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public MeasureBuilder setNumber(String number) {
+            return setStringArgument(TtsSpan.ARG_NUMBER, number);
+        }
+
+        /**
+         * Convenience method that converts the integer part to a String and set
+         * it to the value for {@link TtsSpan#ARG_INTEGER_PART}.
+         * @param integerPart The integer part of a decimal or fraction.
+         * @return This instance.
+         */
+        public MeasureBuilder setIntegerPart(long integerPart) {
+            return setNumber(String.valueOf(integerPart));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_INTEGER_PART} argument.
+         * @param integerPart The integer part of a decimal or fraction; a
+         * non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public MeasureBuilder setIntegerPart(String integerPart) {
+            return setStringArgument(TtsSpan.ARG_INTEGER_PART, integerPart);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_FRACTIONAL_PART} argument.
+         * @param fractionalPart The fractional part of a decimal; a non-empty
+         * string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public MeasureBuilder setFractionalPart(String fractionalPart) {
+            return setStringArgument(TtsSpan.ARG_FRACTIONAL_PART,
+                                     fractionalPart);
+        }
+
+        /**
+         * Convenience method that converts the numerator to a String and set it
+         * to the value for {@link TtsSpan#ARG_NUMERATOR}.
+         * @param numerator The numerator of a fraction.
+         * @return This instance.
+         */
+        public MeasureBuilder setNumerator(long numerator) {
+            return setNumerator(String.valueOf(numerator));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_NUMERATOR} argument.
+         * @param numerator The numerator of a fraction; a non-empty string of
+         *     digits with an optional leading + or -.
+         * @return This instance.
+         */
+        public MeasureBuilder setNumerator(String numerator) {
+            return setStringArgument(TtsSpan.ARG_NUMERATOR, numerator);
+        }
+
+        /**
+         * Convenience method that converts the denominator to a String and set
+         * it to the value for {@link TtsSpan#ARG_DENOMINATOR}.
+         * @param denominator The denominator of a fraction.
+         * @return This instance.
+         */
+        public MeasureBuilder setDenominator(long denominator) {
+            return setDenominator(String.valueOf(denominator));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_DENOMINATOR} argument.
+         * @param denominator The denominator of a fraction; a non-empty string
+         *     of digits with an optional leading + or -.
+         * @return This instance.
+         */
+        public MeasureBuilder setDenominator(String denominator) {
+            return setStringArgument(TtsSpan.ARG_DENOMINATOR, denominator);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_UNIT} argument.
+         * @param unit The unit of the measure.
+         * @return This instance.
+         * @see {@link TtsSpan.ARG_UNIT}
+         */
+        public MeasureBuilder setUnit(String unit) {
+            return setStringArgument(TtsSpan.ARG_UNIT, unit);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_TIME}.
+     */
+    public static class TimeBuilder
+            extends SemioticClassBuilder<TimeBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_TIME}.
+         */
+        public TimeBuilder() {
+            super(TtsSpan.TYPE_TIME);
+        }
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_TIME} and
+         * sets the {@link TtsSpan#ARG_HOURS} and {@link TtsSpan#ARG_MINUTES}
+         * arguments.
+         */
+        public TimeBuilder(int hours, int minutes) {
+            this();
+            setHours(hours);
+            setMinutes(minutes);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_HOURS} argument.
+         * @param hours The value to be set for hours. See
+         * {@link TtsSpan#ARG_HOURS}.
+         * @return This instance.
+         * @see {@link TtsSpan#ARG_HOURS}
+         */
+        public TimeBuilder setHours(int hours) {
+            return setIntArgument(TtsSpan.ARG_HOURS, hours);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_MINUTES} argument.
+         * @param minutes The value to be set for minutes. See
+         * {@link TtsSpan#ARG_MINUTES}.
+         * @return This instance.
+         * @see {@link TtsSpan#ARG_MINUTES}
+         */
+        public TimeBuilder setMinutes(int minutes) {
+            return setIntArgument(TtsSpan.ARG_MINUTES, minutes);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_DATE}.
+     */
+    public static class DateBuilder
+            extends SemioticClassBuilder<DateBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_DATE}.
+         */
+        public DateBuilder() {
+            super(TtsSpan.TYPE_DATE);
+        }
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_TIME} and
+         * possibly sets the {@link TtsSpan#ARG_WEEKDAY},
+         * {@link TtsSpan#ARG_DAY}, {@link TtsSpan#ARG_MONTH} and
+         * {@link TtsSpan#ARG_YEAR} arguments. Pass null to any argument to
+         * leave it unset.
+         */
+        public DateBuilder(Integer weekday,
+                           Integer day,
+                           Integer month,
+                           Integer year) {
+            this();
+            if (weekday != null) {
+                setWeekday(weekday);
+            }
+            if (day != null) {
+                setDay(day);
+            }
+            if (month != null) {
+                setMonth(month);
+            }
+            if (year != null) {
+                setYear(year);
+            }
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_WEEKDAY} argument.
+         * @param weekday The value to be set for weekday. See
+         * {@link TtsSpan#ARG_WEEKDAY}.
+         * @return This instance.
+         * @see {@link TtsSpan#ARG_WEEKDAY}
+         */
+        public DateBuilder setWeekday(int weekday) {
+            return setIntArgument(TtsSpan.ARG_WEEKDAY, weekday);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_DAY} argument.
+         * @param day The value to be set for day. See
+         * {@link TtsSpan#ARG_DAY}.
+         * @return This instance.
+         * @see {@link TtsSpan#ARG_DAY}
+         */
+        public DateBuilder setDay(int day) {
+            return setIntArgument(TtsSpan.ARG_DAY, day);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_MONTH} argument.
+         * @param month The value to be set for month. See
+         * {@link TtsSpan#ARG_MONTH}.
+         * @return This instance.
+         * @see {@link TtsSpan#ARG_MONTH}
+         */
+        public DateBuilder setMonth(int month) {
+            return setIntArgument(TtsSpan.ARG_MONTH, month);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_YEAR} argument.
+         * @param year The value to be set for year. See
+         * {@link TtsSpan#ARG_YEAR}.
+         * @return This instance.
+         * @see {@link TtsSpan#ARG_YEAR}
+         */
+        public DateBuilder setYear(int year) {
+            return setIntArgument(TtsSpan.ARG_YEAR, year);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_DIGITS}.
+     */
+    public static class DigitsBuilder
+            extends SemioticClassBuilder<DigitsBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type
+         * {@link TtsSpan#TYPE_VERBATIM}.
+         */
+        public DigitsBuilder() {
+            super(TtsSpan.TYPE_DIGITS);
+        }
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_DIGITS}
+         * and sets the {@link TtsSpan#ARG_DIGITS} argument.
+         */
+        public DigitsBuilder(String digits) {
+            this();
+            setDigits(digits);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_DIGITS} argument.
+         * @param digits A string of digits.
+         * @return This instance.
+         */
+        public DigitsBuilder setDigits(String digits) {
+            return setStringArgument(TtsSpan.ARG_DIGITS, digits);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_VERBATIM}.
+     */
+    public static class VerbatimBuilder
+            extends SemioticClassBuilder<VerbatimBuilder> {
+
+        /**
+         * Creates a builder for a TtsSpan of type
+         * {@link TtsSpan#TYPE_VERBATIM}.
+         */
+        public VerbatimBuilder() {
+            super(TtsSpan.TYPE_VERBATIM);
+        }
+
+        /**
+         * Creates a builder for a TtsSpan of type {@link TtsSpan#TYPE_VERBATIM}
+         * and sets the {@link TtsSpan#ARG_VERBATIM} argument.
+         */
+        public VerbatimBuilder(String verbatim) {
+            this();
+            setVerbatim(verbatim);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_VERBATIM} argument.
+         * @param verbatim A string of characters that will be read verbatim,
+         *     except whitespace.
+         * @return This instance.
+         */
+        public VerbatimBuilder setVerbatim(String verbatim) {
+            return setStringArgument(TtsSpan.ARG_VERBATIM, verbatim);
+        }
+    }
 }
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index e0e19b9..89d879e 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -90,6 +90,18 @@
     }
 
     /**
+     * Returns the language component of a given locale string.
+     */
+    private static String parseLanguageFromLocaleString(String locale) {
+        final int idx = locale.indexOf('_');
+        if (idx < 0) {
+            return locale;
+        } else {
+            return locale.substring(0, idx);
+        }
+    }
+
+    /**
      * Get a spell checker session for the specified spell checker
      * @param locale the locale for the spell checker. If {@code locale} is null and
      * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
@@ -134,9 +146,8 @@
             }
             if (locale != null) {
                 final String subtypeLocale = subtypeInUse.getLocale();
-                final String inputLocale = locale.toString();
-                if (subtypeLocale.length() < 2 || inputLocale.length() < 2
-                        || !subtypeLocale.substring(0, 2).equals(inputLocale.substring(0, 2))) {
+                final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
+                if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) {
                     return null;
                 }
             }
@@ -145,11 +156,12 @@
             for (int i = 0; i < sci.getSubtypeCount(); ++i) {
                 final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
                 final String tempSubtypeLocale = subtype.getLocale();
+                final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale);
                 if (tempSubtypeLocale.equals(localeStr)) {
                     subtypeInUse = subtype;
                     break;
-                } else if (localeStr.length() >= 2 && tempSubtypeLocale.length() >= 2
-                        && localeStr.startsWith(tempSubtypeLocale)) {
+                } else if (tempSubtypeLanguage.length() >= 2 &&
+                        locale.getLanguage().equals(tempSubtypeLanguage)) {
                     subtypeInUse = subtype;
                 }
             }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index adf4803..9cf3e4f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1945,6 +1945,23 @@
     }
 
     /**
+     * Performs a zoom operation in this WebView.
+     *
+     * @param zoomFactor the zoom factor to apply. The zoom factor will be clamped to the Webview's
+     * zoom limits. This value must be in the range 0.01 to 100.0 inclusive.
+     *
+     * @return false if no zoom changes, true otherwise.
+     */
+    public boolean zoomBy(float zoomFactor) {
+        checkThread();
+        if (zoomFactor < 0.01)
+            throw new IllegalArgumentException("zoomFactor must be greater than 0.01.");
+        if (zoomFactor > 100.0)
+            throw new IllegalArgumentException("zoomFactor must be less than 100.");
+        return mProvider.zoomBy(zoomFactor);
+    }
+
+    /**
      * Performs zoom in in this WebView.
      *
      * @return true if zoom in succeeds, false if no zoom changes
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 13be453..2c7b3eb 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -63,6 +63,11 @@
     private static final Object sProviderLock = new Object();
     private static boolean sAddressSpaceReserved = false;
 
+    public static String getWebViewPackageName() {
+        // TODO: Make this dynamic based on resource configuration.
+        return "com.android.webview";
+    }
+
     static WebViewFactoryProvider getProvider() {
         synchronized (sProviderLock) {
             // For now the main purpose of this function (and the factory abstraction) is to keep
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index b6fd363..13cd2bd 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -238,6 +238,8 @@
 
     public boolean canZoomOut();
 
+    public boolean zoomBy(float zoomFactor);
+
     public boolean zoomIn();
 
     public boolean zoomOut();
diff --git a/core/java/android/widget/LegacyTimePickerDelegate.java b/core/java/android/widget/LegacyTimePickerDelegate.java
index 6dd70ba..70db211 100644
--- a/core/java/android/widget/LegacyTimePickerDelegate.java
+++ b/core/java/android/widget/LegacyTimePickerDelegate.java
@@ -429,11 +429,14 @@
 
     @Override
     public void setShowDoneButton(boolean showDoneButton) {
-        mShowDoneButton = showDoneButton;
-        updateDoneButton();
+        if (mDoneButton != null) {
+            mShowDoneButton = showDoneButton;
+            updateDoneButton();
+        }
     }
 
-    private boolean isShowDoneButton() {
+    @Override
+    public boolean isShowDoneButton() {
         return mShowDoneButton;
     }
 
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 58a6562..026a8ee 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -163,6 +163,13 @@
     /**
      * @hide
      */
+    public boolean isShowDoneButton() {
+        return mDelegate.isShowDoneButton();
+    }
+
+    /**
+     * @hide
+     */
     public void setDismissCallback(TimePickerDismissCallback callback) {
         mDelegate.setDismissCallback(callback);
     }
@@ -234,6 +241,7 @@
         void setEnabled(boolean enabled);
         boolean isEnabled();
 
+        boolean isShowDoneButton();
         void setShowDoneButton(boolean showDoneButton);
         void setDismissCallback(TimePickerDismissCallback callback);
 
diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerDelegate.java
index a9af2f9..45be637 100644
--- a/core/java/android/widget/TimePickerDelegate.java
+++ b/core/java/android/widget/TimePickerDelegate.java
@@ -564,7 +564,8 @@
         return mRadialTimePickerView.getCurrentItemShowing();
     }
 
-    private boolean isShowDoneButton() {
+    @Override
+    public boolean isShowDoneButton() {
         return mShowDoneButton;
     }
 
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 7e58351..50e7bcf 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -271,6 +271,9 @@
                 mFullTargetPackage = null;
                 mSocket.close();
             } catch (IOException e) {
+                if (DEBUG) {
+                    Log.w(TAG, "Exception caught in finishBackup()", e);
+                }
                 return TRANSPORT_ERROR;
             } finally {
                 mSocket = null;
@@ -576,7 +579,7 @@
                 return TRANSPORT_PACKAGE_REJECTED;
             }
             mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
-            mFullRestoreBuffer = new byte[32*1024];
+            mFullRestoreBuffer = new byte[2*1024];
         }
 
         int nRead;
diff --git a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
index 5b542ba..b8fa04c 100644
--- a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
+++ b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
@@ -94,6 +94,7 @@
     JNIEnv* env = NULL;
     int result = attach_thread(&env);
     if (result != JNI_OK) {
+        ALOGE("Unable to attach thread with JNI.");
         return;
     }
 
@@ -215,7 +216,7 @@
         return NULL;
     }
 
-    jclass string_class = env->FindClass("java/lang/String;");
+    jclass string_class = env->FindClass("java/lang/String");
     if (string_class == NULL) {
         ALOGE("Unable to find String class for supported activities.");
         return NULL;
@@ -229,14 +230,8 @@
 
     for (int i = 0; i < list_size; ++i) {
         const char* string_ptr = const_cast<const char*>(list[i]);
-        jsize string_length = strlen(string_ptr);
-        jstring string = env->NewString((const jchar*) string_ptr, string_length);
+        jstring string = env->NewStringUTF(string_ptr);
         env->SetObjectArrayElement(string_array, i, string);
-
-        // log debugging information in case we need to try to trace issues with the strings
-        if (string_length) {
-            ALOGD("Invalid activity (index=%d) name size: %d", i, string_length);
-        }
     }
 
     return string_array;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c9fd6c4..ba65d63 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -481,7 +481,9 @@
         android:label="@string/permlab_writeCallLog"
         android:description="@string/permdesc_writeCallLog" />
 
-  <!-- Allows an application to read from the user's social stream. -->
+  <!-- Allows an application to read from the user's social stream.
+       @deprecated This functionality will be unsupported in the future; cursors returned
+       will be empty. Please do not use. -->
     <permission android:name="android.permission.READ_SOCIAL_STREAM"
         android:permissionGroup="android.permission-group.SOCIAL_INFO"
         android:protectionLevel="dangerous"
@@ -489,7 +491,9 @@
         android:description="@string/permdesc_readSocialStream" />
 
     <!-- Allows an application to write (but not read) the user's
-         social stream data. -->
+         social stream data.
+         @deprecated This functionality will be unsupported in the future; cursors returned
+         will be empty. Please do not use. -->
     <permission android:name="android.permission.WRITE_SOCIAL_STREAM"
         android:permissionGroup="android.permission-group.SOCIAL_INFO"
         android:protectionLevel="dangerous"
@@ -2771,6 +2775,13 @@
         android:description="@string/permdesc_accessDrmCertificates"
         android:protectionLevel="signature|system" />
 
+    <!-- Api Allows an application to create media projection sessions.
+         @hide This is not a third-party API (intended for system apps). -->
+    <permission android:name="android.permission.CREATE_MEDIA_PROJECTION"
+        android:label="@string/permlab_createMediaProjection"
+        android:description="@string/permdesc_createMediaProjection"
+        android:protectionLevel="signature" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/layout-sw720dp/status_bar_latest_event_ticker.xml b/core/res/res/layout-sw720dp/status_bar_latest_event_ticker.xml
deleted file mode 100644
index a09ad0c..0000000
--- a/core/res/res/layout-sw720dp/status_bar_latest_event_ticker.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/status_bar_height"
-    >
-    <ImageView android:id="@+id/icon"
-        android:layout_width="48dp"
-        android:layout_height="match_parent"
-        android:scaleType="center"
-        />
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:paddingStart="16dp"
-        >
-        <TextView android:id="@+id/title"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            />
-        <TextView android:id="@+id/text"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_marginTop="-4dp"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            />
-    </LinearLayout>
-    <TextView android:id="@+id/info"
-        android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="6dip"
-        android:gravity="bottom"
-        android:singleLine="true"
-        />
-</LinearLayout>
diff --git a/core/res/res/layout-sw720dp/status_bar_latest_event_ticker_large_icon.xml b/core/res/res/layout-sw720dp/status_bar_latest_event_ticker_large_icon.xml
deleted file mode 100644
index 09ff1c8..0000000
--- a/core/res/res/layout-sw720dp/status_bar_latest_event_ticker_large_icon.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:paddingStart="16dp"
-        >
-        <TextView android:id="@+id/title"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            />
-        <TextView android:id="@+id/text"
-            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_marginTop="-4dp"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            />
-    </LinearLayout>
-    <TextView android:id="@+id/info"
-        android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="6dip"
-        android:gravity="bottom"
-        android:singleLine="true"
-        />
-    <ImageView android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:layout_marginBottom="13dip"
-        android:scaleType="center"
-        android:layout_marginEnd="4dip"
-        android:layout_marginStart="16dip"
-        />
-</LinearLayout>
-
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2492689..f133707 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3861,6 +3861,11 @@
     <!-- Description of an application permission that lets it control keyguard. -->
     <string name="permdesc_recovery">Allows an application to interact with the recovery system and system updates.</string>
 
+    <!-- Title of an application permission that lets it create media projection sessions. -->
+    <string name="permlab_createMediaProjection">Create media projection sessions</string>
+    <!-- Description of an application permission that lets it create media projection sessions. -->
+    <string name="permdesc_createMediaProjection">Allows an application to create media projection sessions. These sessions can provide applications the ability to capture display and audio contents. Should never be needed by normal apps.</string>
+
     <!-- Shown in the tutorial for tap twice for zoom control. -->
     <string name="tutorial_double_tap_to_zoom_message_short">Touch twice for zoom control</string>
 
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 5531433..b0f5864 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -23,6 +23,7 @@
     </style>
 
     <style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker">
+        <item name="legacyMode">true</item>
         <item name="legacyLayout">@layout/time_picker_legacy_leanback</item>
     </style>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index aaadc16..2ff6777 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1169,8 +1169,6 @@
   <java-symbol type="layout" name="select_dialog" />
   <java-symbol type="layout" name="simple_dropdown_hint" />
   <java-symbol type="layout" name="status_bar_latest_event_content" />
-  <java-symbol type="layout" name="status_bar_latest_event_ticker" />
-  <java-symbol type="layout" name="status_bar_latest_event_ticker_large_icon" />
   <java-symbol type="layout" name="text_edit_action_popup_text" />
   <java-symbol type="layout" name="text_drag_thumbnail" />
   <java-symbol type="layout" name="typing_filter" />
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index 636216d..534e323 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -31,7 +31,6 @@
     </style>
 
     <style name="Theme.Leanback.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker">
-      <item name="legacyMode">true</item>
       <item name="colorBackground">@color/background_leanback_dark</item>
       <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
       <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
@@ -41,7 +40,6 @@
     </style>
 
     <style name="Theme.Leanback.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker">
-      <item name="legacyMode">true</item>
       <item name="colorBackground">@color/background_leanback_light</item>
       <item name="textColorPrimary">@color/primary_text_leanback_light</item>
       <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
old mode 100644
new mode 100755
index e468a754..5a96132
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -259,7 +259,7 @@
     }
 }
 
-void OpenGLRenderer::startTilingCurrentClip(bool opaque) {
+void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
     if (!mSuppressTiling) {
         const Snapshot* snapshot = currentSnapshot();
 
@@ -268,14 +268,27 @@
             clip = &(snapshot->layer->clipRect);
         }
 
-        startTiling(*clip, getViewportHeight(), opaque);
+        startTiling(*clip, getViewportHeight(), opaque, expand);
     }
 }
 
-void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) {
+void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
     if (!mSuppressTiling) {
-        mCaches.startTiling(clip.left, windowHeight - clip.bottom,
+        if(expand) {
+            // Expand the startTiling region by 1
+            int leftNotZero = (clip.left > 0) ? 1 : 0;
+            int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
+
+            mCaches.startTiling(
+                clip.left - leftNotZero,
+                windowHeight - clip.bottom - topNotZero,
+                clip.right - clip.left + leftNotZero + 1,
+                clip.bottom - clip.top + topNotZero + 1,
+                opaque);
+        } else {
+            mCaches.startTiling(clip.left, windowHeight - clip.bottom,
                 clip.right - clip.left, clip.bottom - clip.top, opaque);
+        }
     }
 }
 
@@ -821,7 +834,8 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->getTexture(), 0);
 
-    startTilingCurrentClip(true);
+    // Expand the startTiling region by 1
+    startTilingCurrentClip(true, true);
 
     // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
     mCaches.enableScissor();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
old mode 100644
new mode 100755
index e7328be..4e7844b
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -491,14 +491,14 @@
      * This method needs to be invoked every time getTargetFbo() is
      * bound again.
      */
-    void startTilingCurrentClip(bool opaque = false);
+    void startTilingCurrentClip(bool opaque = false, bool expand = false);
 
     /**
      * Tells the GPU what part of the screen is about to be redrawn.
      * This method needs to be invoked every time getTargetFbo() is
      * bound again.
      */
-    void startTiling(const Rect& clip, int windowHeight, bool opaque = false);
+    void startTiling(const Rect& clip, int windowHeight, bool opaque = false, bool expand = false);
 
     /**
      * Tells the GPU that we are done drawing the frame or that we
diff --git a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
index 8707a10..c7dfc88 100644
--- a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
+++ b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
@@ -40,4 +40,17 @@
     public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() {
         return mActivityRecognitionEvents;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:");
+
+        for (ActivityRecognitionEvent event : mActivityRecognitionEvents) {
+            builder.append("\n    ");
+            builder.append(event.toString());
+        }
+        builder.append("\n]");
+
+        return builder.toString();
+    }
 }
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
index 8c719ce4..a39cff2 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
@@ -41,4 +41,30 @@
     public long getTimestampNs() {
         return mTimestampNs;
     }
+
+    @Override
+    public String toString() {
+        String eventString;
+        switch (mEventType) {
+            case ActivityRecognitionProvider.EVENT_TYPE_ENTER:
+                eventString = "Enter";
+                break;
+            case ActivityRecognitionProvider.EVENT_TYPE_EXIT:
+                eventString = "Exit";
+                break;
+            case ActivityRecognitionProvider.EVENT_TYPE_FLUSH_COMPLETE:
+                eventString = "FlushComplete";
+                break;
+            default:
+                eventString = "<Invalid>";
+                break;
+        }
+
+        return String.format(
+                "Activity='%s', EventType=%s(%s), TimestampNs=%s",
+                mActivity,
+                eventString,
+                mEventType,
+                mTimestampNs);
+    }
 }
diff --git a/media/java/android/media/AudioDevice.java b/media/java/android/media/AudioDevice.java
index 96d6196..e078354 100644
--- a/media/java/android/media/AudioDevice.java
+++ b/media/java/android/media/AudioDevice.java
@@ -19,30 +19,91 @@
 import android.util.SparseIntArray;
 
 /**
- * @hide
- * CANDIDATE FOR PUBLIC API
+ * Class to provide information about the audio devices.
  */
 public class AudioDevice {
 
+    /**
+     * A device type associated with an unknown or uninitialized device.
+     */
     public static final int DEVICE_TYPE_UNKNOWN          = 0;
+    /**
+     * A device type describing the attached earphone speaker.
+     */
     public static final int DEVICE_TYPE_BUILTIN_EARPIECE = 1;
+    /**
+     * A device type describing the speaker system (i.e. a mono speaker or stereo speakers) built
+     * in a device.
+     */
     public static final int DEVICE_TYPE_BUILTIN_SPEAKER  = 2;
+    /**
+     * A device type describing a headset, which is the combination of a headphones and microphone.
+     */
     public static final int DEVICE_TYPE_WIRED_HEADSET    = 3;
+    /**
+     * A device type describing a pair of wired headphones .
+     */
     public static final int DEVICE_TYPE_WIRED_HEADPHONES = 4;
+    /**
+     * A device type describing an analog line-level connection.
+     */
     public static final int DEVICE_TYPE_LINE_ANALOG      = 5;
+    /**
+     * A device type describing a digital line connection (e.g. SPDIF).
+     */
     public static final int DEVICE_TYPE_LINE_DIGITAL     = 6;
+    /**
+     * A device type describing a Bluetooth device typically used for telephony .
+     */
     public static final int DEVICE_TYPE_BLUETOOTH_SCO    = 7;
+    /**
+     * A device type describing a Bluetooth device supporting the A2DP profile.
+     */
     public static final int DEVICE_TYPE_BLUETOOTH_A2DP   = 8;
+    /**
+     * A device type describing an HDMI connection .
+     */
     public static final int DEVICE_TYPE_HDMI             = 9;
+    /**
+     * A device type describing the Audio Return Channel of an HDMI connection.
+     */
     public static final int DEVICE_TYPE_HDMI_ARC         = 10;
+    /**
+     * A device type describing a USB audio device.
+     */
     public static final int DEVICE_TYPE_USB_DEVICE       = 11;
+    /**
+     * A device type describing a USB audio device in accessory mode.
+     */
     public static final int DEVICE_TYPE_USB_ACCESSORY    = 12;
+    /**
+     * A device type describing the audio device associated with a dock.
+     */
     public static final int DEVICE_TYPE_DOCK             = 13;
+    /**
+     * A device type associated with the transmission of audio signals over FM.
+     */
     public static final int DEVICE_TYPE_FM               = 14;
+    /**
+     * A device type describing the microphone(s) built in a device.
+     */
     public static final int DEVICE_TYPE_BUILTIN_MIC      = 15;
+    /**
+     * A device type for accessing the audio content transmitted over FM.
+     */
     public static final int DEVICE_TYPE_FM_TUNER         = 16;
+    /**
+     * A device type for accessing the audio content transmitted over the TV tuner system.
+     */
     public static final int DEVICE_TYPE_TV_TUNER         = 17;
+    /**
+     * A device type describing the transmission of audio signals over the telephony network.
+     */
     public static final int DEVICE_TYPE_TELEPHONY        = 18;
+    /**
+     * A device type describing the auxiliary line-level connectors.
+     */
+    public static final int DEVICE_TYPE_AUX_LINE         = 19;
 
     AudioDevicePortConfig mConfig;
 
@@ -50,18 +111,38 @@
         mConfig = new AudioDevicePortConfig(config);
     }
 
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * @return
+     */
     public boolean isInputDevice() {
         return (mConfig.port().role() == AudioPort.ROLE_SOURCE);
     }
 
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * @return
+     */
     public boolean isOutputDevice() {
         return (mConfig.port().role() == AudioPort.ROLE_SINK);
     }
 
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * @return
+     */
     public int getDeviceType() {
         return INT_TO_EXT_DEVICE_MAPPING.get(mConfig.port().type(), DEVICE_TYPE_UNKNOWN);
     }
 
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * @return
+     */
     public String getAddress() {
         return mConfig.port().address();
     }
@@ -102,6 +183,7 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_HDMI_ARC, DEVICE_TYPE_HDMI_ARC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_SPDIF, DEVICE_TYPE_LINE_DIGITAL);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_FM, DEVICE_TYPE_FM);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_AUX_LINE, DEVICE_TYPE_AUX_LINE);
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, DEVICE_TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, DEVICE_TYPE_BLUETOOTH_SCO);
@@ -143,6 +225,7 @@
         EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
         EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
         EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TELEPHONY, AudioSystem.DEVICE_OUT_TELEPHONY_TX);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_AUX_LINE, AudioSystem.DEVICE_OUT_AUX_LINE);
     }
 }
 
diff --git a/media/java/android/media/audiofx/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
index 136761b..78eeccb 100644
--- a/media/java/android/media/audiofx/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -248,8 +248,6 @@
     }
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
      * Checks if the combination of a channel mask and device type is supported by this virtualizer.
      * Some virtualizer implementations may only support binaural processing (i.e. only support
      * headphone output), some may support transaural processing (i.e. for speaker output) for the
@@ -276,8 +274,6 @@
     }
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
      * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
      * mask and device type.
      * If the virtualization configuration (mask and device) is supported (see
@@ -318,8 +314,6 @@
     }
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
      * Forces the virtualizer effect to use the processing mode used for the given device type.
      * The effect must be enabled for the forced mode to be applied.
      * @param deviceType one of the device types defined in {@link AudioDevice}.
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
new file mode 100644
index 0000000..dd84ad32
--- /dev/null
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.media.projection;
+
+import android.media.projection.IMediaProjectionCallback;
+
+/** {@hide} */
+interface IMediaProjection {
+    void start(IMediaProjectionCallback callback);
+    void stop();
+    boolean canProjectAudio();
+    boolean canProjectVideo();
+    boolean canProjectSecureVideo();
+    int getVirtualDisplayFlags();
+    void addCallback(IMediaProjectionCallback callback);
+    void removeCallback(IMediaProjectionCallback callback);
+}
diff --git a/media/java/android/media/projection/IMediaProjectionCallback.aidl b/media/java/android/media/projection/IMediaProjectionCallback.aidl
new file mode 100644
index 0000000..f3743d1
--- /dev/null
+++ b/media/java/android/media/projection/IMediaProjectionCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.media.projection;
+
+/** {@hide} */
+oneway interface IMediaProjectionCallback {
+    void onStop();
+}
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
new file mode 100644
index 0000000..6ed803a
--- /dev/null
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.media.projection;
+
+import android.media.projection.IMediaProjection;
+import android.os.IBinder;
+
+/** {@hide} */
+interface IMediaProjectionManager {
+    boolean hasProjectionPermission(int uid, String packageName);
+    IMediaProjection createProjection(int uid, String packageName, int type,
+            boolean permanentGrant);
+    boolean isValidMediaProjection(IMediaProjection projection);
+}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
new file mode 100644
index 0000000..348a577
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -0,0 +1,198 @@
+/*
+ * 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.media.projection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.AudioRecord;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.Map;
+
+/**
+ * A token granting applications the ability to capture screen contents and/or
+ * record system audio. The exact capabilities granted depend on the type of
+ * MediaProjection.
+ *
+ * <p>
+ * A screen capture session can be started through {@link
+ * MediaProjectionManager#getScreenCaptureIntent}. This grants the ability to
+ * capture screen contents, but not system audio.
+ * </p>
+ */
+public final class MediaProjection {
+    private static final String TAG = "MediaProjection";
+
+    private final IMediaProjection mImpl;
+    private final Context mContext;
+    private final Map<Callback, CallbackRecord> mCallbacks;
+
+    /** @hide */
+    public MediaProjection(Context context, IMediaProjection impl) {
+        mCallbacks = new ArrayMap<Callback, CallbackRecord>();
+        mContext = context;
+        mImpl = impl;
+        try {
+            mImpl.start(new MediaProjectionCallback());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failed to start media projection", e);
+        }
+    }
+
+    /** Register a listener to receive notifications about when the {@link
+     * MediaProjection} changes state.
+     *
+     * @param callback The callback to call.
+     * @param handler The handler on which the callback should be invoked, or
+     * null if the callback should be invoked on the calling thread's looper.
+     *
+     * @see #removeCallback
+     */
+    public void addCallback(Callback callback, Handler handler) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback should not be null");
+        }
+        mCallbacks.put(callback, new CallbackRecord(callback, handler));
+    }
+
+    /** Unregister a MediaProjection listener.
+     *
+     * @param callback The callback to unregister.
+     *
+     * @see #addCallback
+     */
+    public void removeCallback(Callback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback should not be null");
+        }
+        mCallbacks.remove(callback);
+    }
+
+    /**
+     * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
+     * contents of the screen.
+     *
+     * @param name The name of the virtual display, must be non-empty.
+     * @param width The width of the virtual display in pixels. Must be
+     * greater than 0.
+     * @param height The height of the virtual display in pixels. Must be
+     * greater than 0.
+     * @param dpi The density of the virtual display in dpi. Must be greater
+     * than 0.
+     * @param surface The surface to which the content of the virtual display
+     * should be rendered, or null if there is none initially.
+     * @param isSecure Whether the display should be considered a secure
+     * display. This typically requires special permissions not available to
+     * third party applications.
+     * @param callbacks Callbacks to call when the virtual display's state
+     * changes, or null if none.
+     * @param handler The {@link android.os.Handler} on which the callback should be
+     * invoked, or null if the callback should be invoked on the calling
+     * thread's main {@link android.os.Looper}.
+     *
+     * @see android.hardware.display.DisplayManager#createVirtualDisplay(
+     * String, int, int, int, int, Surface, VirtualDisplay.Callbacks, Handler)
+     */
+    public VirtualDisplay createVirtualDisplay(@NonNull String name,
+            int width, int height, int dpi, boolean isSecure, @Nullable Surface surface,
+            @Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) {
+        DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+        int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0;
+        return dm.createVirtualDisplay(
+                    this, name, width, height, dpi, surface, flags, callbacks, handler);
+    }
+
+    /**
+     * Creates an AudioRecord to capture audio played back by the system.
+     */
+    public AudioRecord createAudioRecord(
+            int sampleRateInHz, int channelConfig,
+            int audioFormat, int bufferSizeInBytes) {
+        return null;
+    }
+
+    /**
+     * Stops projection.
+     */
+    public void stop() {
+        try {
+            mImpl.stop();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to stop projection", e);
+        }
+    }
+
+    /**
+     * Get the underlying IMediaProjection.
+     * @hide
+     */
+    public IMediaProjection getProjection() {
+        return mImpl;
+    }
+
+    /**
+     * Callbacks for the projection session.
+     */
+    public static abstract class Callback {
+        /**
+         * Called when the MediaProjection session is no longer valid.
+         *
+         * Once a MediaProjection has been stopped, it's up to the application to release any
+         * resources it may be holding (e.g. {@link android.hardware.display.VirtualDisplay}s).
+         */
+        public void onStop() { }
+    }
+
+    private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
+        @Override
+        public void onStop() {
+            final int N = mCallbacks.size();
+            for (int i = 0; i < N; i++) {
+                mCallbacks.get(i).onStop();
+            }
+        }
+    }
+
+    private final static class CallbackRecord {
+        private Callback mCallback;
+        private Handler mHandler;
+
+        public CallbackRecord(Callback callback, Handler handler) {
+            mCallback = callback;
+            mHandler = handler;
+        }
+
+        public void onStop() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onStop();
+                }
+            });
+        }
+    }
+}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
new file mode 100644
index 0000000..aac8cf9
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -0,0 +1,91 @@
+/*
+ * 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.media.projection;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.projection.IMediaProjection;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Manages the retrieval of certain types of {@link MediaProjection} tokens.
+ *
+ * <p>
+ * Get an instance of this class by calling {@link
+ * android.content.Context#getSystemService(java.lang.String)
+ * Context.getSystemService()} with the argument {@link
+ * android.content.Context#MEDIA_PROJECTION_SERVICE}.
+ * </p>
+ */
+public final class MediaProjectionManager {
+    /** @hide */
+    public static final String EXTRA_APP_TOKEN = "android.media.projection.extra.EXTRA_APP_TOKEN";
+    /** @hide */
+    public static final String EXTRA_MEDIA_PROJECTION =
+            "android.media.projection.extra.EXTRA_MEDIA_PROJECTION";
+
+    /** @hide */
+    public static final int TYPE_SCREEN_CAPTURE = 0;
+    /** @hide */
+    public static final int TYPE_MIRRORING = 1;
+    /** @hide */
+    public static final int TYPE_PRESENTATION = 2;
+
+    private Context mContext;
+
+    /** @hide */
+    public MediaProjectionManager(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Returns an Intent that <b>must</b> passed to startActivityForResult()
+     * in order to start screen capture. The activity will prompt
+     * the user whether to allow screen capture.  The result of this
+     * activity should be passed to getMediaProjection.
+     */
+    public Intent getScreenCaptureIntent() {
+        Intent i = new Intent();
+        i.setClassName("com.android.systemui",
+                "com.android.systemui.media.MediaProjectionPermissionActivity");
+        return i;
+    }
+
+    /**
+     * Retrieve the MediaProjection obtained from a succesful screen
+     * capture request. Will be null if the result from the
+     * startActivityForResult() is anything other than RESULT_OK.
+     *
+     * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int,
+     * int, android.content.Intent)}
+     * @param resultData The resulting data from {@link android.app.Activity#onActivityResult(int,
+     * int, android.content.Intent)}
+     */
+    public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
+        if (resultCode != Activity.RESULT_OK || resultData == null) {
+            return null;
+        }
+        IBinder projection = resultData.getIBinderExtra(EXTRA_MEDIA_PROJECTION);
+        if (projection == null) {
+            return null;
+        }
+        return new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection));
+    }
+}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 4841360..086cd23 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -154,7 +154,7 @@
         if (TextUtils.isEmpty(tag)) {
             throw new IllegalArgumentException("tag cannot be null or empty");
         }
-        mCbStub = new CallbackStub();
+        mCbStub = new CallbackStub(this);
         MediaSessionManager manager = (MediaSessionManager) context
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
         try {
@@ -769,7 +769,7 @@
     public static class CallbackStub extends ISessionCallback.Stub {
         private WeakReference<MediaSession> mMediaSession;
 
-        public void setMediaSession(MediaSession session) {
+        public CallbackStub(MediaSession session) {
             mMediaSession = new WeakReference<MediaSession>(session);
         }
 
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 423e317..403b1ba 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -36,4 +36,5 @@
     void onTrackInfoChanged(in List<TvTrackInfo> tracks, int seq);
     void onVideoAvailable(int seq);
     void onVideoUnavailable(int reason, int seq);
+    void onContentBlocked(in String rating, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 6a0c592..16c4199 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -34,6 +34,7 @@
  */
 interface ITvInputManager {
     List<TvInputInfo> getTvInputList(int userId);
+    TvInputInfo getTvInputInfo(in String inputId, int userId);
 
     void registerCallback(in ITvInputManagerCallback callback, int userId);
     void unregisterCallback(in ITvInputManagerCallback callback, int userId);
@@ -42,6 +43,8 @@
     void releaseSession(in IBinder sessionToken, int userId);
 
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
+    void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
+            int userId);
     void setVolume(in IBinder sessionToken, float volume, int userId);
     void tune(in IBinder sessionToken, in Uri channelUri, int userId);
     void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
@@ -53,6 +56,8 @@
     void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
     void removeOverlayView(in IBinder sessionToken, int userId);
 
+    void unblockContent(in IBinder sessionToken, in String unblockedRating, int userId);
+
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
     /*
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 5c8a0a3..50a7636 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -22,4 +22,6 @@
  */
 oneway interface ITvInputManagerCallback {
     void onInputStateChanged(in String inputId, int state);
+    void onInputAdded(in String inputId);
+    void onInputRemoved(in String inputId);
 }
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index 992e424..158223a 100644
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -18,6 +18,7 @@
 
 import android.media.tv.ITvInputServiceCallback;
 import android.media.tv.ITvInputSessionCallback;
+import android.media.tv.TvInputHardwareInfo;
 import android.view.InputChannel;
 
 /**
@@ -27,5 +28,10 @@
 oneway interface ITvInputService {
     void registerCallback(ITvInputServiceCallback callback);
     void unregisterCallback(in ITvInputServiceCallback callback);
-    void createSession(in InputChannel channel, ITvInputSessionCallback callback);
+    void createSession(in InputChannel channel, ITvInputSessionCallback callback,
+            in String inputId);
+
+    // For hardware TvInputService
+    void notifyHardwareAdded(in TvInputHardwareInfo info);
+    void notifyHardwareRemoved(int deviceId);
 }
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 1fdb8c5..287da71 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -16,13 +16,14 @@
 
 package android.media.tv;
 
-import android.content.ComponentName;
+import android.media.tv.TvInputInfo;
 
 /**
- * Helper interface for ITvInputService to allow the TV input to notify the client when its status
- * has been changed.
+ * Helper interface for ITvInputService to allow the service to notify the
+ * TvInputManagerService.
  * @hide
  */
 oneway interface ITvInputServiceCallback {
-    void onInputStateChanged(int state);
+    void addTvInput(in TvInputInfo inputInfo);
+    void removeTvInput(in String inputId);
 }
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 875fa34..a97daf2 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -29,6 +29,7 @@
     void release();
 
     void setSurface(in Surface surface);
+    void dispatchSurfaceChanged(int format, int width, int height);
     // TODO: Remove this once it becomes irrelevant for applications to handle audio focus. The plan
     // is to introduce some new concepts that will solve a number of problems in audio policy today.
     void setVolume(float volume);
@@ -40,4 +41,6 @@
     void createOverlayView(in IBinder windowToken, in Rect frame);
     void relayoutOverlayView(in Rect frame);
     void removeOverlayView();
+
+    void unblockContent(in String unblockedRating);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index e0036e1..f529595 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -33,4 +33,5 @@
     void onTrackInfoChanged(in List<TvTrackInfo> tracks);
     void onVideoAvailable();
     void onVideoUnavailable(int reason);
+    void onContentBlocked(in String rating);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 0c08590..eddc6c9 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -42,14 +42,16 @@
 
     private static final int DO_RELEASE = 1;
     private static final int DO_SET_SURFACE = 2;
-    private static final int DO_SET_VOLUME = 3;
-    private static final int DO_TUNE = 4;
-    private static final int DO_SET_CAPTION_ENABLED = 5;
-    private static final int DO_SELECT_TRACK = 6;
-    private static final int DO_UNSELECT_TRACK = 7;
-    private static final int DO_CREATE_OVERLAY_VIEW = 8;
-    private static final int DO_RELAYOUT_OVERLAY_VIEW = 9;
-    private static final int DO_REMOVE_OVERLAY_VIEW = 10;
+    private static final int DO_DISPATCH_SURFACE_CHANGED = 3;
+    private static final int DO_SET_VOLUME = 4;
+    private static final int DO_TUNE = 5;
+    private static final int DO_SET_CAPTION_ENABLED = 6;
+    private static final int DO_SELECT_TRACK = 7;
+    private static final int DO_UNSELECT_TRACK = 8;
+    private static final int DO_CREATE_OVERLAY_VIEW = 9;
+    private static final int DO_RELAYOUT_OVERLAY_VIEW = 10;
+    private static final int DO_REMOVE_OVERLAY_VIEW = 11;
+    private static final int DO_UNBLOCK_CONTENT = 12;
 
     private final HandlerCaller mCaller;
 
@@ -91,6 +93,12 @@
                 mTvInputSessionImpl.setSurface((Surface) msg.obj);
                 return;
             }
+            case DO_DISPATCH_SURFACE_CHANGED: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mTvInputSessionImpl.dispatchSurfaceChanged(args.argi1, args.argi2, args.argi3);
+                args.recycle();
+                return;
+            }
             case DO_SET_VOLUME: {
                 mTvInputSessionImpl.setVolume((Float) msg.obj);
                 return;
@@ -125,6 +133,10 @@
                 mTvInputSessionImpl.removeOverlayView(true);
                 return;
             }
+            case DO_UNBLOCK_CONTENT: {
+                mTvInputSessionImpl.unblockContent((String) msg.obj);
+                return;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 return;
@@ -143,6 +155,12 @@
     }
 
     @Override
+    public void dispatchSurfaceChanged(int format, int width, int height) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED,
+                format, width, height, 0));
+    }
+
+    @Override
     public final void setVolume(float volume) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_VOLUME, volume));
     }
@@ -183,6 +201,11 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
     }
 
+    @Override
+    public void unblockContent(String unblockedRating) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UNBLOCK_CONTENT, unblockedRating));
+    }
+
     private final class TvInputEventReceiver extends InputEventReceiver {
         public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 905b0bd..986a7d9 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -16,18 +16,20 @@
 
 package android.media.tv;
 
+import android.annotation.SystemApi;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 /**
  * A class representing a TV content rating.
  */
-public class TvContentRating {
+public final class TvContentRating {
     private static final String TAG = "TvContentRating";
 
     private static final int RATING_PREFIX_LENGTH = 10;
@@ -124,10 +126,10 @@
     // A mapping from two-letter country code (ISO 3166-1 alpha-2) to its rating-to-sub-ratings map.
     // This is used for validating the builder parameters.
     private static final Map<String, Map<String, String[]>> sRatings
-            = new HashMap<String, Map<String, String[]>>();
+            = new ArrayMap<String, Map<String, String[]>>();
 
     static {
-        Map<String, String[]> usRatings = new HashMap<String, String[]>();
+        Map<String, String[]> usRatings = new ArrayMap<String, String[]>();
         usRatings.put(RATING_US_TV_Y, null);
         usRatings.put(RATING_US_TV_Y7, new String[] { SUBRATING_US_FV });
         usRatings.put(RATING_US_TV_G, null);
@@ -139,7 +141,7 @@
                 SUBRATING_US_L, SUBRATING_US_S, SUBRATING_US_V });
         sRatings.put(PREFIX_RATING_US, usRatings);
 
-        Map<String, String[]> krRatings = new HashMap<String, String[]>();
+        Map<String, String[]> krRatings = new ArrayMap<String, String[]>();
         krRatings.put(RATING_KR_ALL, null);
         krRatings.put(RATING_KR_7, null);
         krRatings.put(RATING_KR_12, null);
@@ -157,8 +159,7 @@
      * @param rating The rating constant defined in this class.
      */
     public TvContentRating(String rating) {
-        mRating = rating;
-        mSubRatings = null;
+        this(rating, null);
     }
 
     /**
@@ -168,11 +169,11 @@
      * @param subRatings The String array of sub-rating constants defined in this class.
      */
     public TvContentRating(String rating, String[] subRatings) {
-        mRating = rating;
-        mSubRatings = subRatings;
-        if (TextUtils.isEmpty(mRating)) {
+        if (TextUtils.isEmpty(rating)) {
             throw new IllegalArgumentException("rating cannot be null");
         }
+        mRating = rating;
+        mSubRatings = subRatings;
         String prefix = "";
         if (mRating.length() > RATING_PREFIX_LENGTH) {
             prefix = mRating.substring(0, RATING_PREFIX_LENGTH);
@@ -188,6 +189,10 @@
                 } else {
                     List<String> validSubRatingList = Arrays.asList(subRatings);
                     for (String sr : mSubRatings) {
+                        if (TextUtils.isEmpty(sr)) {
+                            throw new IllegalArgumentException(
+                                    "subRatings cannot contain empty elements");
+                        }
                         if (!validSubRatingList.contains(sr)) {
                             Log.w(TAG, "Invalid subrating: " + sr);
                             break;
@@ -201,6 +206,52 @@
     }
 
     /**
+     * Returns the main rating constant.
+     *
+     * @return the rating string that starts with "RATING_" prefix as defined in this class.
+     */
+    public String getMainRating() {
+        return mRating;
+    }
+
+    /**
+     * Returns the list of sub-rating constants.
+     *
+     * @return the unmodifiable {@code List} of sub-rating strings that start with "SUBRATING_"
+     *         prefix as defined in this class.
+     */
+    public List<String> getSubRatings() {
+        if (mSubRatings == null) {
+            return null;
+        }
+        return Collections.unmodifiableList(Arrays.asList(mSubRatings));
+    }
+
+
+    /**
+     * Returns a String that unambiguously describes both the rating and sub-rating information
+     * contained in the TvContentRating. You can later recover the TvContentRating from this string
+     * through {@link #unflattenFromString}.
+     *
+     * @return a new String holding rating/sub-rating information, which can later be stored in the
+     *         database and settings.
+     * @see #unflattenFromString
+     */
+    public String flattenToString() {
+        // TODO: Consider removing all obvious/redundant sub-strings including "RATING" and
+        // "SUBRATING" and find out a storage-efficient string format such as:
+        // <country>-<primary>/<sub1>/<sub2>/<sub3>
+        StringBuilder builder = new StringBuilder(mRating);
+        if (mSubRatings != null) {
+            for (String subRating : mSubRatings) {
+                builder.append(DELIMITER);
+                builder.append(subRating);
+            }
+        }
+        return builder.toString();
+    }
+
+    /**
      * Recovers a TvContentRating from a String that was previously created with
      * {@link #flattenToString}.
      *
@@ -226,20 +277,35 @@
     }
 
     /**
-     * @return a String that unambiguously describes both the rating and sub-rating information
-     *         contained in the TvContentRating. You can later recover the TvContentRating from this
-     *         string through {@link #unflattenFromString}.
-     * @see #unflattenFromString
+     * Returns true if this rating has the same main rating as the specified rating and when this
+     * rating's sub-ratings contain the other's.
+     * <p>
+     * For example, a TvContentRating object that represents TV-PG with S(Sexual content) and
+     * V(Violence) contains TV-PG, TV-PG/S, TV-PG/V and itself.
+     * </p>
+     *
+     * @param rating The {@link TvContentRating} to check.
+     * @return {@code true} if this object contains {@code rating}, {@code false} otherwise.
+     * @hide
      */
-    public String flattenToString() {
-        StringBuffer ratingStr = new StringBuffer();
-        ratingStr.append(mRating);
-        if (mSubRatings != null) {
-            for (String subRating : mSubRatings) {
-                ratingStr.append(DELIMITER);
-                ratingStr.append(subRating);
-            }
+    @SystemApi
+    public final boolean contains(TvContentRating rating) {
+        if (rating == null) {
+            throw new IllegalArgumentException("rating cannot be null");
         }
-        return ratingStr.toString();
+        if (!rating.getMainRating().equals(mRating)) {
+            return false;
+        }
+        List<String> subRatings = getSubRatings();
+        List<String> subRatingsOther = rating.getSubRatings();
+        if (subRatings == null && subRatingsOther == null) {
+            return true;
+        } else if (subRatings == null && subRatingsOther != null) {
+            return false;
+        } else if (subRatings != null && subRatingsOther == null) {
+            return true;
+        } else {
+            return subRatings.containsAll(subRatingsOther);
+        }
     }
 }
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 8ecf808..78e0404 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,9 +16,9 @@
 
 package android.media.tv;
 
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentUris;
-import android.media.tv.TvContract.Programs;
 import android.net.Uri;
 import android.provider.BaseColumns;
 import android.util.ArraySet;
@@ -53,6 +53,7 @@
     private static final String PATH_CHANNEL = "channel";
     private static final String PATH_PROGRAM = "program";
     private static final String PATH_INPUT = "input";
+    private static final String PATH_PASSTHROUGH = "passthrough";
 
     /**
      * An optional query, update or delete URI parameter that allows the caller to specify start
@@ -88,6 +89,16 @@
     public static final String PARAM_CANONICAL_GENRE = "canonical_genre";
 
     /**
+     * Builds an ID that uniquely identifies a TV input service.
+     *
+     * @param name The {@link ComponentName} of the TV input service to build ID for.
+     * @return the ID for the given TV input service.
+     */
+    public static final String buildInputId(ComponentName name) {
+        return name.flattenToShortString();
+    }
+
+    /**
      * Builds a URI that points to a specific channel.
      *
      * @param channelId The ID of the channel to point to.
@@ -97,6 +108,18 @@
     }
 
     /**
+     * Build a special channel URI intended to be used with pass-through type inputs. (e.g. HDMI)
+     *
+     * @param inputId The ID of the TV input to build a channels URI for.
+     * @see TvInputInfo#isPassthroughInputType()
+     */
+    public static final Uri buildChannelUriForPassthroughTvInput(String inputId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+                .appendPath(PATH_INPUT).appendPath(inputId).appendPath(PATH_CHANNEL)
+                .appendPath(PATH_PASSTHROUGH).build();
+    }
+
+    /**
      * 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.
@@ -118,12 +141,12 @@
     }
 
     /**
-     * Builds a URI that points to all browsable channels from a given TV input.
+     * Builds a URI that points to all channels from a given TV input.
      *
      * @param inputId The ID of the TV input to build a channels URI for.
      */
     public static final Uri buildChannelsUriForInput(String inputId) {
-        return buildChannelsUriForInput(inputId, true);
+        return buildChannelsUriForInput(inputId, false);
     }
 
     /**
@@ -133,6 +156,7 @@
      * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
      *            to {@code false} the URI points to all channels regardless of whether they are
      *            browsable or not.
+     * @hide
      */
     public static final Uri buildChannelsUriForInput(String inputId, boolean browsableOnly) {
         return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
@@ -327,68 +351,65 @@
         /** The channel type for SECAM. */
         public static final int TYPE_SECAM = 0x3;
 
-        /** The special channel type used for pass-through inputs such as HDMI. */
-        public static final int TYPE_PASSTHROUGH = 0x00010000;
-
         /** The channel type for DVB-T (terrestrial). */
-        public static final int TYPE_DVB_T = 0x00020000;
+        public static final int TYPE_DVB_T = 0x00010000;
 
         /** The channel type for DVB-T2 (terrestrial). */
-        public static final int TYPE_DVB_T2 = 0x00020001;
+        public static final int TYPE_DVB_T2 = 0x00010001;
 
         /** The channel type for DVB-S (satellite). */
-        public static final int TYPE_DVB_S = 0x00020100;
+        public static final int TYPE_DVB_S = 0x00010100;
 
         /** The channel type for DVB-S2 (satellite). */
-        public static final int TYPE_DVB_S2 = 0x00020101;
+        public static final int TYPE_DVB_S2 = 0x00010101;
 
         /** The channel type for DVB-C (cable). */
-        public static final int TYPE_DVB_C = 0x00020200;
+        public static final int TYPE_DVB_C = 0x00010200;
 
         /** The channel type for DVB-C2 (cable). */
-        public static final int TYPE_DVB_C2 = 0x00020201;
+        public static final int TYPE_DVB_C2 = 0x00010201;
 
         /** The channel type for DVB-H (handheld). */
-        public static final int TYPE_DVB_H = 0x00020300;
+        public static final int TYPE_DVB_H = 0x00010300;
 
         /** The channel type for DVB-SH (satellite). */
-        public static final int TYPE_DVB_SH = 0x00020400;
+        public static final int TYPE_DVB_SH = 0x00010400;
 
         /** The channel type for ATSC (terrestrial). */
-        public static final int TYPE_ATSC_T = 0x00030000;
+        public static final int TYPE_ATSC_T = 0x00020000;
 
         /** The channel type for ATSC (cable). */
-        public static final int TYPE_ATSC_C = 0x00030200;
+        public static final int TYPE_ATSC_C = 0x00020200;
 
         /** The channel type for ATSC-M/H (mobile/handheld). */
-        public static final int TYPE_ATSC_M_H = 0x00030300;
+        public static final int TYPE_ATSC_M_H = 0x00020300;
 
         /** The channel type for ISDB-T (terrestrial). */
-        public static final int TYPE_ISDB_T = 0x00040000;
+        public static final int TYPE_ISDB_T = 0x00030000;
 
         /** The channel type for ISDB-Tb (Brazil). */
-        public static final int TYPE_ISDB_TB = 0x00040100;
+        public static final int TYPE_ISDB_TB = 0x00030100;
 
         /** The channel type for ISDB-S (satellite). */
-        public static final int TYPE_ISDB_S = 0x00040200;
+        public static final int TYPE_ISDB_S = 0x00030200;
 
         /** The channel type for ISDB-C (cable). */
-        public static final int TYPE_ISDB_C = 0x00040300;
+        public static final int TYPE_ISDB_C = 0x00030300;
 
         /** The channel type for 1seg (handheld). */
-        public static final int TYPE_1SEG = 0x00040400;
+        public static final int TYPE_1SEG = 0x00030400;
 
         /** The channel type for DTMB (terrestrial). */
-        public static final int TYPE_DTMB = 0x00050000;
+        public static final int TYPE_DTMB = 0x00040000;
 
         /** The channel type for CMMB (handheld). */
-        public static final int TYPE_CMMB = 0x00050100;
+        public static final int TYPE_CMMB = 0x00040100;
 
         /** The channel type for T-DMB (terrestrial). */
-        public static final int TYPE_T_DMB = 0x00060000;
+        public static final int TYPE_T_DMB = 0x00050000;
 
         /** The channel type for S-DMB (satellite). */
-        public static final int TYPE_S_DMB = 0x00060100;
+        public static final int TYPE_S_DMB = 0x00050100;
 
         /** A generic service type. */
         public static final int SERVICE_TYPE_OTHER = 0x0;
@@ -475,7 +496,9 @@
         }
 
         /**
-         * The ID of the TV input that provides this TV channel.
+         * The ID of the TV input service that provides this TV channel.
+         * <p>
+         * Use {@link #buildInputId} to build the ID.
          * <p>
          * This is a required field.
          * </p><p>
@@ -488,14 +511,13 @@
          * The predefined type of this TV channel.
          * <p>
          * This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the
-         * current channel conforms to, with an exception being {@link #TYPE_PASSTHROUGH}, which is
-         * a special channel type used only by pass-through inputs such as HDMI. The value should
-         * match to one of the followings: {@link #TYPE_OTHER}, {@link #TYPE_PASSTHROUGH},
-         * {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S}, {@link #TYPE_DVB_S2},
-         * {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H}, {@link #TYPE_DVB_SH},
-         * {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C}, {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T},
-         * {@link #TYPE_ISDB_TB}, {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C} {@link #TYPE_1SEG},
-         * {@link #TYPE_DTMB}, {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB}
+         * current channel conforms to. The value should match to one of the followings:
+         * {@link #TYPE_OTHER}, {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S},
+         * {@link #TYPE_DVB_S2}, {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H},
+         * {@link #TYPE_DVB_SH}, {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C},
+         * {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T}, {@link #TYPE_ISDB_TB},
+         * {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C}, {@link #TYPE_1SEG}, {@link #TYPE_DTMB},
+         * {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB}
          * </p><p>
          * This is a required field.
          * </p><p>
@@ -644,6 +666,7 @@
          * </p><p>
          * Type: INTEGER (boolean)
          * </p>
+         * @hide
          */
         public static final String COLUMN_BROWSABLE = "browsable";
 
diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java
index e5f9889..d7d3cf2 100644
--- a/media/java/android/media/tv/TvInputHardwareInfo.java
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.SystemApi;
 import android.media.AudioManager;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,6 +28,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class TvInputHardwareInfo implements Parcelable {
     static final String TAG = "TvInputHardwareInfo";
 
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 37f166b..e7d8b4e 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -27,6 +27,8 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.media.tv.TvInputHardwareInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -55,17 +57,45 @@
     // Should be in sync with hardware/libhardware/include/hardware/tv_input.h
 
     /**
-     * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
+     * TV input type: a generic hardware TV input type.
      */
-    public static final int TYPE_HDMI = 1;
+    public static final int TYPE_OTHER_HARDWARE = 1;
     /**
      * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner)
      */
     public static final int TYPE_TUNER = 2;
     /**
-     * TV input type: the TV input service is stateless pass-through. (e.g. RGB, composite, etc.)
+     * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
      */
-    public static final int TYPE_PASSTHROUGH = 3;
+    public static final int TYPE_HDMI = 3;
+    /**
+     * TV input type: the TV input service represents a display port.
+     */
+    public static final int TYPE_DISPLAY_PORT = 4;
+    /**
+     * TV input type: the TV input service represents a SCART port.
+     */
+    public static final int TYPE_SCART = 5;
+    /**
+     * TV input type: the TV input service represents a DVI port.
+     */
+    public static final int TYPE_DVI = 6;
+    /**
+     * TV input type: the TV input service represents a VGA port.
+     */
+    public static final int TYPE_VGA = 7;
+    /**
+     * TV input type: the TV input service represents a component port.
+     */
+    public static final int TYPE_COMPONENT = 8;
+    /**
+     * TV input type: the TV input service represents a composite port.
+     */
+    public static final int TYPE_COMPOSITE = 9;
+    /**
+     * TV input type: the TV input service represents a SVIDEO port.
+     */
+    public static final int TYPE_SVIDEO = 10;
 
     /**
      * The ID of the TV input to provide to the setup activity and settings activity.
@@ -88,9 +118,46 @@
      * instantiating it from the given Context and ResolveInfo.
      *
      * @param service The ResolveInfo returned from the package manager about this TV input service.
-     * @hide */
+     * @hide
+     */
     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service)
             throws XmlPullParserException, IOException {
+        return createTvInputInfo(context, service, generateInputIdForComponentName(
+                new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)));
+    }
+
+    /**
+     * Create a new instance of the TvInputInfo class,
+     * instantiating it from the given Context, ResolveInfo, and HdmiCecDeviceInfo.
+     *
+     * @param service The ResolveInfo returned from the package manager about this TV input service.
+     * @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device.
+     * @hide
+     */
+    public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
+            HdmiCecDeviceInfo cecInfo) throws XmlPullParserException, IOException {
+        return createTvInputInfo(context, service, generateInputIdForHdmiCec(
+                new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
+                cecInfo));
+    }
+
+    /**
+     * Create a new instance of the TvInputInfo class,
+     * instantiating it from the given Context, ResolveInfo, and TvInputHardwareInfo.
+     *
+     * @param service The ResolveInfo returned from the package manager about this TV input service.
+     * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
+     * @hide
+     */
+    public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
+            TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException {
+        return createTvInputInfo(context, service, generateInputIdForHardware(
+                new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
+                hardwareInfo));
+    }
+
+    private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
+            String id) throws XmlPullParserException, IOException {
         ServiceInfo si = service.serviceInfo;
         PackageManager pm = context.getPackageManager();
         XmlResourceParser parser = null;
@@ -115,7 +182,7 @@
                         "Meta-data does not start with tv-input-service tag in " + si.name);
             }
 
-            TvInputInfo input = new TvInputInfo(context, service, null);
+            TvInputInfo input = new TvInputInfo(context, service, id, null);
             TypedArray sa = res.obtainAttributes(attrs,
                     com.android.internal.R.styleable.TvInputService);
             input.mSetupActivity = sa.getString(
@@ -153,12 +220,13 @@
      * Constructor.
      *
      * @param service The ResolveInfo returned from the package manager about this TV input service.
-     * @hide
+     * @param id ID of this TV input. Should be generated via generateInputId*().
+     * @param parentId ID of this TV input's parent input. {@code null} if none exists.
      */
-    private TvInputInfo(Context context, ResolveInfo service, String parentId) {
+    private TvInputInfo(Context context, ResolveInfo service, String id, String parentId) {
         mService = service;
         ServiceInfo si = service.serviceInfo;
-        mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
+        mId = id;
         mParentId = parentId;
     }
 
@@ -237,26 +305,41 @@
     }
 
     /**
+     * Returns {@code true} if this TV input is pass-though which does not have any real channels
+     * in TvProvider. {@code false} otherwise.
+     *
+     * @see TvContract#buildChannelUriForPassthroughTvInput(String)
+     */
+    public boolean isPassthroughInputType() {
+        if (mType == TYPE_HDMI || mType == TYPE_DISPLAY_PORT || mType == TYPE_SCART
+                || mType == TYPE_DVI || mType == TYPE_VGA || mType == TYPE_COMPONENT
+                || mType == TYPE_COMPOSITE || mType == TYPE_SVIDEO) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Loads the user-displayed label for this TV input service.
      *
-     * @param pm Supplies a PackageManager used to load the TV input's resources.
+     * @param context Supplies a {@link Context} used to load the label.
      * @return a CharSequence containing the TV input's label. If the TV input does not have
      *         a label, its name is returned.
      */
-    public CharSequence loadLabel(PackageManager pm) {
-        return mService.loadLabel(pm);
+    public CharSequence loadLabel(Context context) {
+        return mService.loadLabel(context.getPackageManager());
     }
 
     /**
      * Loads the user-displayed icon for this TV input service.
      *
-     * @param pm Supplies a PackageManager used to load the TV input's resources.
+     * @param context Supplies a {@link Context} used to load the icon.
      * @return a Drawable containing the TV input's icon. If the TV input does not have
      *         an icon, application icon is returned. If it's unavailable too, system default is
      *         returned.
      */
-    public Drawable loadIcon(PackageManager pm) {
-        return mService.serviceInfo.loadIcon(pm);
+    public Drawable loadIcon(Context context) {
+        return mService.serviceInfo.loadIcon(context.getPackageManager());
     }
 
     @Override
@@ -311,13 +394,37 @@
      *
      * @param name the component name for generating an input id.
      * @return the generated input id for the given {@code name}.
-     * @hide
      */
-    public static final String generateInputIdForComponentName(ComponentName name) {
+    private static final String generateInputIdForComponentName(ComponentName name) {
         return name.flattenToShortString();
     }
 
     /**
+     * Used to generate an input id from a ComponentName and HdmiCecDeviceInfo.
+     *
+     * @param name the component name for generating an input id.
+     * @param cecInfo HdmiCecDeviceInfo describing this TV input.
+     * @return the generated input id for the given {@code name} and {@code cecInfo}.
+     */
+    private static final String generateInputIdForHdmiCec(
+            ComponentName name, HdmiCecDeviceInfo cecInfo) {
+        return name.flattenToShortString() + String.format("|CEC%08X%08X",
+                cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress());
+    }
+
+    /**
+     * Used to generate an input id from a ComponentName and TvInputHardwareInfo
+     *
+     * @param name the component name for generating an input id.
+     * @param hardwareInfo TvInputHardwareInfo describing this TV input.
+     * @return the generated input id for the given {@code name} and {@code hardwareInfo}.
+     */
+    private static final String generateInputIdForHardware(
+            ComponentName name, TvInputHardwareInfo hardwareInfo) {
+        return name.flattenToShortString() + String.format("|HW%d", hardwareInfo.getDeviceId());
+    }
+
+    /**
      * Used to make this class parcelable.
      *
      * @hide
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 79a83b0..910b725 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -36,7 +36,6 @@
 import android.view.View;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -49,10 +48,13 @@
 public final class TvInputManager {
     private static final String TAG = "TvInputManager";
 
+    static final int VIDEO_UNAVAILABLE_REASON_START = 0;
+    static final int VIDEO_UNAVAILABLE_REASON_END = 3;
+
     /**
      * A generic reason. Video is not available due to an unspecified error.
      */
-    public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0;
+    public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = VIDEO_UNAVAILABLE_REASON_START;
     /**
      * Video is not available because the TV input is tuning to another channel.
      */
@@ -65,13 +67,13 @@
      * Video is not available because the TV input stopped the playback temporarily to buffer more
      * data.
      */
-    public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3;
+    public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = VIDEO_UNAVAILABLE_REASON_END;
 
     /**
      * The TV input is connected.
      * <p>
      * State for {@link #getInputState} and {@link
-     * TvInputManager.TvInputListener#onInputStateChanged}.
+     * TvInputManager.TvInputCallback#onInputStateChanged}.
      * </p>
      */
     public static final int INPUT_STATE_CONNECTED = 0;
@@ -80,7 +82,7 @@
      * fully ready.
      * <p>
      * State for {@link #getInputState} and {@link
-     * TvInputManager.TvInputListener#onInputStateChanged}.
+     * TvInputManager.TvInputCallback#onInputStateChanged}.
      * </p>
      */
     public static final int INPUT_STATE_CONNECTED_STANDBY = 1;
@@ -88,7 +90,7 @@
      * The TV input is disconnected.
      * <p>
      * State for {@link #getInputState} and {@link
-     * TvInputManager.TvInputListener#onInputStateChanged}.
+     * TvInputManager.TvInputCallback#onInputStateChanged}.
      * </p>
      */
     public static final int INPUT_STATE_DISCONNECTED = 2;
@@ -98,8 +100,8 @@
     private final Object mLock = new Object();
 
     // @GuardedBy(mLock)
-    private final List<TvInputListenerRecord> mTvInputListenerRecordsList =
-            new LinkedList<TvInputListenerRecord>();
+    private final List<TvInputCallbackRecord> mTvInputCallbackRecordsList =
+            new LinkedList<TvInputCallbackRecord>();
 
     // A mapping from TV input ID to the state of corresponding input.
     // @GuardedBy(mLock)
@@ -185,6 +187,15 @@
         }
 
         /**
+         * This is called when the current program content is blocked by parental controls.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback
+         * @param rating The content ration of the blocked program.
+         */
+        public void onContentBlocked(Session session, TvContentRating rating) {
+        }
+
+        /**
          * This is called when a custom event has been sent from this session.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback
@@ -263,6 +274,15 @@
             });
         }
 
+        public void postContentBlocked(final TvContentRating rating) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onContentBlocked(mSession, rating);
+                }
+            });
+        }
+
         public void postSessionEvent(final String eventType, final Bundle eventArgs) {
             mHandler.post(new Runnable() {
                 @Override
@@ -276,12 +296,12 @@
     /**
      * Interface used to monitor status of the TV input.
      */
-    public abstract static class TvInputListener {
+    public abstract static class TvInputCallback {
         /**
          * This is called when the state of a given TV input is changed.
          *
-         * @param inputId the id of the TV input.
-         * @param state state of the TV input. The value is one of the following:
+         * @param inputId The id of the TV input.
+         * @param state State of the TV input. The value is one of the following:
          * <ul>
          * <li>{@link TvInputManager#INPUT_STATE_CONNECTED}
          * <li>{@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY}
@@ -290,26 +310,60 @@
          */
         public void onInputStateChanged(String inputId, int state) {
         }
+
+        /**
+         * This is called when a TV input is added.
+         *
+         * @param inputId The id of the TV input.
+         */
+        public void onInputAdded(String inputId) {
+        }
+
+        /**
+         * This is called when a TV input is removed.
+         *
+         * @param inputId The id of the TV input.
+         */
+        public void onInputRemoved(String inputId) {
+        }
     }
 
-    private static final class TvInputListenerRecord {
-        private final TvInputListener mListener;
+    private static final class TvInputCallbackRecord {
+        private final TvInputCallback mCallback;
         private final Handler mHandler;
 
-        public TvInputListenerRecord(TvInputListener listener, Handler handler) {
-            mListener = listener;
+        public TvInputCallbackRecord(TvInputCallback callback, Handler handler) {
+            mCallback = callback;
             mHandler = handler;
         }
 
-        public TvInputListener getListener() {
-            return mListener;
+        public TvInputCallback getCallback() {
+            return mCallback;
         }
 
-        public void postStateChanged(final String inputId, final int state) {
+        public void postInputStateChanged(final String inputId, final int state) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mListener.onInputStateChanged(inputId, state);
+                    mCallback.onInputStateChanged(inputId, state);
+                }
+            });
+        }
+
+        public void postInputAdded(final String inputId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onInputAdded(inputId);
+                }
+            });
+        }
+
+        public void postInputRemoved(final String inputId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onInputRemoved(inputId);
                 }
             });
         }
@@ -403,6 +457,18 @@
             }
 
             @Override
+            public void onContentBlocked(String rating, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postContentBlocked(TvContentRating.unflattenFromString(rating));
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -419,8 +485,28 @@
             public void onInputStateChanged(String inputId, int state) {
                 synchronized (mLock) {
                     mStateMap.put(inputId, state);
-                    for (TvInputListenerRecord record : mTvInputListenerRecordsList) {
-                        record.postStateChanged(inputId, state);
+                    for (TvInputCallbackRecord record : mTvInputCallbackRecordsList) {
+                        record.postInputStateChanged(inputId, state);
+                    }
+                }
+            }
+
+            @Override
+            public void onInputAdded(String inputId) {
+                synchronized (mLock) {
+                    mStateMap.put(inputId, INPUT_STATE_CONNECTED);
+                    for (TvInputCallbackRecord record : mTvInputCallbackRecordsList) {
+                        record.postInputAdded(inputId);
+                    }
+                }
+            }
+
+            @Override
+            public void onInputRemoved(String inputId) {
+                synchronized (mLock) {
+                    mStateMap.remove(inputId);
+                    for (TvInputCallbackRecord record : mTvInputCallbackRecordsList) {
+                        record.postInputRemoved(inputId);
                     }
                 }
             }
@@ -446,6 +532,20 @@
     }
 
     /**
+     * Returns the {@link TvInputInfo} for a given TV input.
+     *
+     * @param inputId The ID of the TV input.
+     * @return the {@link TvInputInfo} for a given TV input. {@code null} if not found.
+     */
+    public TvInputInfo getTvInputInfo(String inputId) {
+        try {
+            return mService.getTvInputInfo(inputId, mUserId);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Returns the state of a given TV input. It retuns one of the following:
      * <ul>
      * <li>{@link #INPUT_STATE_CONNECTED}
@@ -453,7 +553,7 @@
      * <li>{@link #INPUT_STATE_DISCONNECTED}
      * </ul>
      *
-     * @param inputId the id of the TV input.
+     * @param inputId The id of the TV input.
      * @throws IllegalArgumentException if the argument is {@code null} or if there is no
      *        {@link TvInputInfo} corresponding to {@code inputId}.
      */
@@ -471,39 +571,39 @@
     }
 
     /**
-     * Registers a {@link TvInputListener}.
+     * Registers a {@link TvInputCallback}.
      *
-     * @param listener a listener used to monitor status of the TV inputs.
-     * @param handler a {@link Handler} that the status change will be delivered to.
+     * @param callback A callback used to monitor status of the TV inputs.
+     * @param handler A {@link Handler} that the status change will be delivered to.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void registerListener(TvInputListener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
+    public void registerCallback(TvInputCallback callback, Handler handler) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
         }
         if (handler == null) {
             throw new IllegalArgumentException("handler cannot be null");
         }
         synchronized (mLock) {
-            mTvInputListenerRecordsList.add(new TvInputListenerRecord(listener, handler));
+            mTvInputCallbackRecordsList.add(new TvInputCallbackRecord(callback, handler));
         }
     }
 
     /**
-     * Unregisters the existing {@link TvInputListener}.
+     * Unregisters the existing {@link TvInputCallback}.
      *
-     * @param listener the existing listener to remove.
+     * @param callback The existing callback to remove.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void unregisterListener(final TvInputListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
+    public void unregisterCallback(final TvInputCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
         }
         synchronized (mLock) {
-            for (Iterator<TvInputListenerRecord> it = mTvInputListenerRecordsList.iterator();
+            for (Iterator<TvInputCallbackRecord> it = mTvInputCallbackRecordsList.iterator();
                     it.hasNext(); ) {
-                TvInputListenerRecord record = it.next();
-                if (record.getListener() == listener) {
+                TvInputCallbackRecord record = it.next();
+                if (record.getCallback() == callback) {
                     it.remove();
                     break;
                 }
@@ -518,9 +618,9 @@
      * the given TV input.
      * </p>
      *
-     * @param inputId the id of the TV input.
-     * @param callback a callback used to receive the created session.
-     * @param handler a {@link Handler} that the session creation will be delivered to.
+     * @param inputId The id of the TV input.
+     * @param callback A callback used to receive the created session.
+     * @param handler A {@link Handler} that the session creation will be delivered to.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      * @hide
      */
@@ -623,6 +723,27 @@
         }
 
         /**
+         * Notifies of any structural changes (format or size) of the {@link Surface}
+         * passed by {@link #setSurface}.
+         *
+         * @param format The new PixelFormat of the {@link Surface}.
+         * @param width The new width of the {@link Surface}.
+         * @param height The new height of the {@link Surface}.
+         * @hide
+         */
+        public void dispatchSurfaceChanged(int format, int width, int height) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
          * Sets the relative stream volume of this session to handle a change of audio focus.
          *
          * @param volume A volume value between 0.0f to 1.0f.
@@ -685,7 +806,7 @@
         /**
          * Select a track.
          *
-         * @param track the track to be selected.
+         * @param track The track to be selected.
          * @see #getTracks()
          */
         public void selectTrack(TvTrackInfo track) {
@@ -706,7 +827,7 @@
         /**
          * Unselect a track.
          *
-         * @param track the track to be selected.
+         * @param track The track to be selected.
          * @see #getTracks()
          */
         public void unselectTrack(TvTrackInfo track) {
@@ -811,12 +932,27 @@
         }
 
         /**
+         * Unblock content blocked by parental controls.
+         */
+        void unblockContent(TvContentRating unblockedRating) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.unblockContent(mToken, unblockedRating.flattenToString(), mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
          * Dispatches an input event to this session.
          *
-         * @param event {@link InputEvent} to dispatch.
+         * @param event An {@link InputEvent} to dispatch.
          * @param token A token used to identify the input event later in the callback.
          * @param callback A callback used to receive the dispatch result.
-         * @param handler {@link Handler} that the dispatch result will be delivered to.
+         * @param handler A {@link Handler} that the dispatch result will be delivered to.
          * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
          *         {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
          *         {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
@@ -860,7 +996,7 @@
             /**
              * Called when the dispatched input event is finished.
              *
-             * @param token a token passed to {@link #dispatchInputEvent}.
+             * @param token A token passed to {@link #dispatchInputEvent}.
              * @param handled {@code true} if the dispatched input event was handled properly.
              *            {@code false} otherwise.
              */
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 3206320..4f1f988 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -17,8 +17,8 @@
 package android.media.tv;
 
 import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.app.Service;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PixelFormat;
@@ -104,7 +104,8 @@
             }
 
             @Override
-            public void createSession(InputChannel channel, ITvInputSessionCallback cb) {
+            public void createSession(InputChannel channel, ITvInputSessionCallback cb,
+                    String inputId) {
                 if (channel == null) {
                     Log.w(TAG, "Creating session without input channel");
                 }
@@ -114,8 +115,21 @@
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = channel;
                 args.arg2 = cb;
+                args.arg3 = inputId;
                 mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
             }
+
+            @Override
+            public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
+                mHandler.obtainMessage(ServiceHandler.DO_ADD_TV_INPUT_FROM_HARDWARE,
+                        hardwareInfo).sendToTarget();
+            }
+
+            @Override
+            public void notifyHardwareRemoved(int deviceId) {
+                mHandler.obtainMessage(ServiceHandler.DO_REMOVE_TV_INPUT_FROM_HARDWARE,
+                        deviceId, 0).sendToTarget();
+            }
         };
     }
 
@@ -134,8 +148,33 @@
      * <p>
      * May return {@code null} if this TV input service fails to create a session for some reason.
      * </p>
+     * @param inputId The ID of the TV input associated with the session.
      */
-    public abstract Session onCreateSession();
+    public abstract Session onCreateSession(String inputId);
+
+    /**
+     * Returns a new TvInputInfo object if this service is responsible for {@code hardwareInfo};
+     * otherwise, return {@code null}. Override to modify default behavior of ignoring all input.
+     *
+     * @param hardwareInfo TvInputHardwareInfo object just added.
+     *
+     * @hide
+     */
+    @SystemApi
+    public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) {
+        return null;
+    }
+
+    /**
+     * Returns the input ID for {@code deviceId} if it is handled by this service;
+     * otherwise, return {@code null}. Override to modify default behavior of ignoring all input.
+     *
+     * @hide
+     */
+    @SystemApi
+    public String onHardwareRemoved(int deviceId) {
+        return null;
+    }
 
     /**
      * Base class for derived classes to implement to provide a TV input session.
@@ -267,6 +306,42 @@
         }
 
         /**
+         * Informs the application that the current program content is blocked by parent controls.
+         * <p>
+         * Each TV input service is required to query the system whether the user is allowed to
+         * watch the current program before showing it to the user if the parental control is turned
+         * on, which can be checked by calling {@link TvParentalControlManager#isEnabled}. Whether
+         * the TV input service should block the content or not is determined by invoking
+         * {@link TvParentalControlManager#isRatingBlocked} with the content rating for the current
+         * program. Then the TvParentalControlManager makes a judgment based on the user blocked
+         * ratings stored in the secure settings and returns the result. If the rating in question
+         * turns out to be blocked, the TV input service must immediately block the content and call
+         * this method with the content rating of the current program to prompt the PIN verification
+         * screen.
+         * </p><p>
+         * Each TV input service also needs to continuously listen to any changes made to the
+         * parental control settings by registering a
+         * {@link TvParentalControlManager.ParentalControlCallback} to the manager and immediately
+         * reevaluate the current program with the new parental control settings.
+         * </p>
+         *
+         * @param rating The content rating for the current TV program.
+         */
+        public void dispatchContentBlocked(final TvContentRating rating) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "dispatchContentBlocked");
+                        mSessionCallback.onContentBlocked(rating.flattenToString());
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in dispatchContentBlocked");
+                    }
+                }
+            });
+        }
+
+        /**
          * Informs the application that video is not available, so the TV input cannot continue
          * playing the TV stream.
          *
@@ -279,10 +354,8 @@
          * </ul>
          */
         public void dispatchVideoUnavailable(final int reason) {
-            if (reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN
-                    && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNE
-                    && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL
-                    && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING) {
+            if (reason < TvInputManager.VIDEO_UNAVAILABLE_REASON_START
+                    || reason > TvInputManager.VIDEO_UNAVAILABLE_REASON_END) {
                 throw new IllegalArgumentException("Unknown reason: " + reason);
             }
             mHandler.post(new Runnable() {
@@ -313,6 +386,18 @@
         public abstract boolean onSetSurface(Surface surface);
 
         /**
+         * Called after any structural changes (format or size) have been made to the
+         * {@link Surface} passed by {@link #onSetSurface}. This method is always called
+         * at least once, after {@link #onSetSurface} with non-null {@link Surface} is called.
+         *
+         * @param format The new PixelFormat of the {@link Surface}.
+         * @param width The new width of the {@link Surface}.
+         * @param height The new height of the {@link Surface}.
+         */
+        public void onSurfaceChanged(int format, int width, int height) {
+        }
+
+        /**
          * Sets the relative stream volume of the current TV input session to handle the change of
          * audio focus by setting.
          *
@@ -342,6 +427,21 @@
         public abstract void onSetCaptionEnabled(boolean enabled);
 
         /**
+         * Called when the user allowed to unblock the content.
+         * <p>
+         * The implementation should unblock the content.
+         * TV input service has responsibility to decide when/how the unblock expires
+         * while it can keep previously unblocked ratings in order not to ask a user
+         * to unblock whenever a content rating is changed.
+         * Therefore an unblocked rating can be valid for a channel, a program,
+         * or certain amount of time depending on the implementation.
+         * </p>
+         *
+         * @param unblockedRating An unblocked content rating
+         */
+        public abstract void onContentUnblocked(TvContentRating unblockedRating);
+
+        /**
          * Selects a given track.
          * <p>
          * If it is called multiple times on the same type of track (ie. Video, Audio, Text), the
@@ -526,6 +626,17 @@
         }
 
         /**
+         * Calls {@link #onSurfaceChanged}.
+         */
+        void dispatchSurfaceChanged(int format, int width, int height) {
+            if (DEBUG) {
+                Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
+                        + ", height=" + height + ")");
+            }
+            onSurfaceChanged(format, width, height);
+        }
+
+        /**
          * Calls {@link #onSetStreamVolume}.
          */
         void setVolume(float volume) {
@@ -564,6 +675,14 @@
         }
 
         /**
+         * Calls {@link #onContentUnblocked}.
+         */
+        void unblockContent(String unblockedRating) {
+            onContentUnblocked(TvContentRating.unflattenFromString(unblockedRating));
+            // TODO: Handle failure.
+        }
+
+        /**
          * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
          * to the overlay window.
          *
@@ -715,6 +834,32 @@
     @SuppressLint("HandlerLeak")
     private final class ServiceHandler extends Handler {
         private static final int DO_CREATE_SESSION = 1;
+        private static final int DO_ADD_TV_INPUT_FROM_HARDWARE = 2;
+        private static final int DO_REMOVE_TV_INPUT_FROM_HARDWARE = 3;
+
+        private void broadcastAddTvInput(TvInputInfo inputInfo) {
+            int n = mCallbacks.beginBroadcast();
+            for (int i = 0; i < n; ++i) {
+                try {
+                    mCallbacks.getBroadcastItem(i).addTvInput(inputInfo);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error while broadcasting: " + e);
+                }
+            }
+            mCallbacks.finishBroadcast();
+        }
+
+        private void broadcastRemoveTvInput(String inputId) {
+            int n = mCallbacks.beginBroadcast();
+            for (int i = 0; i < n; ++i) {
+                try {
+                    mCallbacks.getBroadcastItem(i).removeTvInput(inputId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error while broadcasting: " + e);
+                }
+            }
+            mCallbacks.finishBroadcast();
+        }
 
         @Override
         public final void handleMessage(Message msg) {
@@ -723,8 +868,9 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     InputChannel channel = (InputChannel) args.arg1;
                     ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
+                    String inputId = (String) args.arg3;
                     try {
-                        Session sessionImpl = onCreateSession();
+                        Session sessionImpl = onCreateSession(inputId);
                         if (sessionImpl == null) {
                             // Failed to create a session.
                             cb.onSessionCreated(null);
@@ -740,6 +886,22 @@
                     args.recycle();
                     return;
                 }
+                case DO_ADD_TV_INPUT_FROM_HARDWARE: {
+                    TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
+                    TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
+                    if (inputInfo != null) {
+                        broadcastAddTvInput(inputInfo);
+                    }
+                    return;
+                }
+                case DO_REMOVE_TV_INPUT_FROM_HARDWARE: {
+                    int deviceId = msg.arg1;
+                    String inputId = onHardwareRemoved(deviceId);
+                    if (inputId != null) {
+                        broadcastRemoveTvInput(inputId);
+                    }
+                    return;
+                }
                 default: {
                     Log.w(TAG, "Unhandled message code: " + msg.what);
                     return;
diff --git a/media/java/android/media/tv/TvParentalControlManager.java b/media/java/android/media/tv/TvParentalControlManager.java
new file mode 100644
index 0000000..3741991
--- /dev/null
+++ b/media/java/android/media/tv/TvParentalControlManager.java
@@ -0,0 +1,268 @@
+/*
+ * 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.media.tv;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Contains methods for accessing and monitoring the user's parental control settings.
+ * <p>
+ * To obtain a handle to the TV parental control manager, do the following:
+ * <p>
+ * <code>
+ * <pre>TvParentalControlManager tvParentalControlManager =
+ *        (TvParentalControlManager) context.getSystemService(Context.TV_PARENTAL_CONTROL_SERVICE);
+ * </pre>
+ * </code>
+ */
+public final class TvParentalControlManager {
+    /** Default parental control enabled value. */
+    private static final int DEFAULT_ENABLED = 0;
+
+    private final Handler mHandler = new Handler();
+
+    private final ContentResolver mContentResolver;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final List<ParentalControlCallbackRecord> mParentalControlCallbackRecordList =
+            new LinkedList<ParentalControlCallbackRecord>();
+
+    @GuardedBy("mLock")
+    private final List<TvContentRating> mBlockedRatings = new ArrayList<TvContentRating>();
+
+    @GuardedBy("mLock")
+    private String mBlockedRatingsString;
+
+    /**
+     * Creates a new parental control manager for the specified context.
+     *
+     * @hide
+     */
+    public TvParentalControlManager(Context context) {
+        mContentResolver = context.getContentResolver();
+    }
+
+    /**
+     * Returns the user's parental control enabled state.
+     *
+     * @return {@code true} if the user enabled the parental control, {@code false} otherwise.
+     */
+    public final boolean isEnabled() {
+        return Settings.Secure.getInt(mContentResolver, Settings.Secure.TV_PARENTAL_CONTROL_ENABLED,
+                DEFAULT_ENABLED) == 1;
+    }
+
+    /**
+     * Checks whether a given TV content rating is blocked by the user.
+     *
+     * @param rating The TV content rating to check.
+     * @return {@code true} if blocked, {@code false} if not blocked or parental control is
+     *         disabled.
+     */
+    public final boolean isRatingBlocked(TvContentRating rating) {
+        if (!isEnabled()) {
+            // Parental control is disabled. Enjoy watching good stuff.
+            return false;
+        }
+
+        // Update the blocked ratings only when they change.
+        final String blockedRatingsString = Settings.Secure.getString(mContentResolver,
+                Settings.Secure.TV_PARENTAL_CONTROL_BLOCKED_RATINGS);
+        synchronized (mLock) {
+            if (!TextUtils.equals(blockedRatingsString, mBlockedRatingsString)) {
+                mBlockedRatingsString = blockedRatingsString;
+                updateBlockedRatingsLocked();
+            }
+            for (TvContentRating blockedRating : mBlockedRatings) {
+                if (rating.contains(blockedRating)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void updateBlockedRatingsLocked() {
+        mBlockedRatings.clear();
+        if (TextUtils.isEmpty(mBlockedRatingsString)) {
+            return;
+        }
+        for (String blockedRatingString : mBlockedRatingsString.split("\\s*,\\s*")) {
+            mBlockedRatings.add(TvContentRating.unflattenFromString(blockedRatingString));
+        }
+    }
+
+    /**
+     * Adds a callback for monitoring the changes in the user's parental control settings.
+     *
+     * @param callback The callback to add.
+     * @param handler a {@link Handler} that the settings change will be delivered to.
+     */
+    public void addParentalControlCallback(ParentalControlCallback callback,
+            Handler handler) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler cannot be null");
+        }
+        synchronized (mLock) {
+            if (mParentalControlCallbackRecordList.isEmpty()) {
+                registerObserver(Settings.Secure.TV_PARENTAL_CONTROL_ENABLED);
+                registerObserver(Settings.Secure.TV_PARENTAL_CONTROL_BLOCKED_RATINGS);
+            }
+            mParentalControlCallbackRecordList.add(
+                    new ParentalControlCallbackRecord(callback, handler));
+        }
+    }
+
+    private void registerObserver(String key) {
+        mContentResolver.registerContentObserver(Settings.Secure.getUriFor(key), false,
+                mContentObserver);
+    }
+
+    /**
+     * Removes a callback previously added using {@link #addParentalControlCallback}.
+     *
+     * @param callback The callback to remove.
+     */
+    public void removeParentalControlCallback(ParentalControlCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        synchronized (mLock) {
+            for (Iterator<ParentalControlCallbackRecord> it =
+                    mParentalControlCallbackRecordList.iterator(); it.hasNext();) {
+                ParentalControlCallbackRecord record = it.next();
+                if (record.getCallback() == callback) {
+                    it.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    private void notifyEnabledChanged() {
+        final boolean enabled = isEnabled();
+        synchronized (mLock) {
+            for (ParentalControlCallbackRecord record : mParentalControlCallbackRecordList) {
+                record.postEnabledChanged(enabled);
+            }
+        }
+    }
+
+    private final ContentObserver mContentObserver = new ContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            final String uriPath = uri.getPath();
+            final String name = uriPath.substring(uriPath.lastIndexOf('/') + 1);
+            if (Settings.Secure.TV_PARENTAL_CONTROL_ENABLED.equals(name)) {
+                notifyEnabledChanged();
+            } else if (Settings.Secure.TV_PARENTAL_CONTROL_BLOCKED_RATINGS.equals(name)) {
+                // We only need a single callback when multiple ratings change in rapid
+                // succession.
+                mHandler.removeCallbacks(mBlockedRatingsChangedRunnable);
+                mHandler.post(mBlockedRatingsChangedRunnable);
+            }
+        }
+    };
+
+    /**
+     * Runnable posted when user blocked ratings change. This is used to prevent unnecessary change
+     * notifications when multiple ratings change in rapid succession.
+     */
+    private final Runnable mBlockedRatingsChangedRunnable = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                for (ParentalControlCallbackRecord record : mParentalControlCallbackRecordList) {
+                    record.postBlockedRatingsChanged();
+                }
+            }
+        }
+    };
+
+    /**
+     * Callback for changes in parental control settings, including enabled state.
+     */
+    public static abstract class ParentalControlCallback {
+        /**
+         * Called when the parental control enabled state changes.
+         *
+         * @param enabled the user's parental control enabled state
+         */
+        public void onEnabledChanged(boolean enabled) {}
+
+        /**
+         * Called when the user blocked ratings change.
+         * <p>
+         * When this is invoked, one should immediately call
+         * {@link TvParentalControlManager#isRatingBlocked} to reevaluate the current content since
+         * the user might have changed her mind and blocked the rating for the content.
+         *
+         * @see TvParentalControlManager#isRatingBlocked
+         */
+        public void onBlockedRatingsChanged() {}
+    }
+
+    private static final class ParentalControlCallbackRecord {
+        private final ParentalControlCallback mCallback;
+        private final Handler mHandler;
+
+        public ParentalControlCallbackRecord(ParentalControlCallback callback, Handler handler) {
+            mCallback = callback;
+            mHandler = handler;
+        }
+
+        public ParentalControlCallback getCallback() {
+            return mCallback;
+        }
+
+        public void postEnabledChanged(final boolean enabled) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onEnabledChanged(enabled);
+                }
+            });
+        }
+
+        public void postBlockedRatingsChanged() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onBlockedRatingsChanged();
+                }
+            });
+        }
+    }
+}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 4ac1ba4..9fbb3b2 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -62,7 +62,7 @@
     private static final int VIDEO_SIZE_VALUE_UNKNOWN = 0;
 
     private final Handler mHandler = new Handler();
-    private TvInputManager.Session mSession;
+    private Session mSession;
     private final SurfaceView mSurfaceView;
     private Surface mSurface;
     private boolean mOverlayViewCreated;
@@ -75,17 +75,21 @@
     private float mStreamVolume;
     private int mVideoWidth = VIDEO_SIZE_VALUE_UNKNOWN;
     private int mVideoHeight = VIDEO_SIZE_VALUE_UNKNOWN;
+    private boolean mSurfaceChanged;
+    private int mSurfaceFormat;
+    private int mSurfaceWidth;
+    private int mSurfaceHeight;
 
     private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
         @Override
         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
             Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
                     + ", height=" + height + ")");
-            if (holder.getSurface() == mSurface) {
-                return;
-            }
-            mSurface = holder.getSurface();
-            setSessionSurface(mSurface);
+            mSurfaceFormat = format;
+            mSurfaceWidth = width;
+            mSurfaceHeight = height;
+            mSurfaceChanged = true;
+            dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
         }
 
         @Override
@@ -97,6 +101,7 @@
         @Override
         public void surfaceDestroyed(SurfaceHolder holder) {
             mSurface = null;
+            mSurfaceChanged = false;
             setSessionSurface(null);
         }
     };
@@ -215,6 +220,22 @@
     }
 
     /**
+     * Unblock TV content.
+     * <p>
+     * This notifies TV input that blocked content is now OK to play.
+     * </p>
+     *
+     * @param unblockedRating a TvContentRating to unblock.
+     * @see TvInputService.Session#dispatchContentBlocked(TvContentRating)
+     * @hide
+     */
+    public void unblockContent(TvContentRating unblockedRating) {
+        if (mSession != null) {
+            mSession.unblockContent(unblockedRating);
+        }
+    }
+
+    /**
      * Enables or disables the caption in this TvView.
      * <p>
      * Note that this method does not take any effect unless the current TvView is tuned.
@@ -423,6 +444,13 @@
         mSession.setSurface(surface);
     }
 
+    private void dispatchSurfaceChanged(int format, int width, int height) {
+        if (mSession == null) {
+            return;
+        }
+        mSession.dispatchSurfaceChanged(format, width, height);
+    }
+
     private void createSessionOverlayView() {
         if (mSession == null || !isAttachedToWindow()
                 || mOverlayViewCreated) {
@@ -548,6 +576,15 @@
         }
 
         /**
+         * This is called when the current program content is blocked by parental controls.
+         *
+         * @param inputId The ID of the TV input bound to this view.
+         * @param rating The content rating of the blocked program.
+         */
+        public void onContentBlocked(String inputId, TvContentRating rating) {
+        }
+
+        /**
          * This is invoked when a custom event from the bound TV input is sent to this view.
          *
          * @param eventType The type of the event.
@@ -600,6 +637,9 @@
                 // setSessionSurface will be called in surfaceCreated.
                 if (mSurface != null) {
                     setSessionSurface(mSurface);
+                    if (mSurfaceChanged) {
+                        dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+                    }
                 }
                 createSessionOverlayView();
                 mSession.tune(mChannelUri);
@@ -615,9 +655,10 @@
 
         @Override
         public void onSessionReleased(Session session) {
-            if (this == mSessionCallback) {
-                mSessionCallback = null;
+            if (this != mSessionCallback) {
+                return;
             }
+            mSessionCallback = null;
             mSession = null;
             if (mListener != null) {
                 mListener.onError(mInputId, ERROR_TV_INPUT_DISCONNECTED);
@@ -626,6 +667,9 @@
 
         @Override
         public void onChannelRetuned(Session session, Uri channelUri) {
+            if (this != mSessionCallback) {
+                return;
+            }
             if (DEBUG) {
                 Log.d(TAG, "onChannelChangedByTvInput(" + channelUri + ")");
             }
@@ -648,7 +692,11 @@
             }
         }
 
+        @Override
         public void onVideoAvailable(Session session) {
+            if (this != mSessionCallback) {
+                return;
+            }
             if (DEBUG) {
                 Log.d(TAG, "onVideoAvailable()");
             }
@@ -657,7 +705,11 @@
             }
         }
 
+        @Override
         public void onVideoUnavailable(Session session, int reason) {
+            if (this != mSessionCallback) {
+                return;
+            }
             if (DEBUG) {
                 Log.d(TAG, "onVideoUnavailable(" + reason + ")");
             }
@@ -667,11 +719,22 @@
         }
 
         @Override
-        public void onSessionEvent(TvInputManager.Session session, String eventType,
-                Bundle eventArgs) {
+        public void onContentBlocked(Session session, TvContentRating rating) {
+            if (DEBUG) {
+                Log.d(TAG, "onContentBlocked()");
+            }
+            if (mListener != null) {
+                mListener.onContentBlocked(mInputId, rating);
+            }
+        }
+
+        public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
             if (this != mSessionCallback) {
                 return;
             }
+            if (DEBUG) {
+                Log.d(TAG, "onSessionEvent(" + eventType + ")");
+            }
             if (mListener != null) {
                 mListener.onEvent(mInputId, eventType, eventArgs);
             }
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 2c1db02..09525b2 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -97,6 +97,16 @@
     }
 
     @Override
+    public void onBackPressed() {
+        WebView myWebView = (WebView) findViewById(R.id.webview);
+        if (myWebView.canGoBack()) {
+            myWebView.goBack();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == R.id.action_use_network) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
index ce47b3e..4a23ec4 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
@@ -311,7 +311,7 @@
         }
     }
 
-    private final class PreloadController implements RecyclerView.OnScrollListener {
+    private final class PreloadController extends RecyclerView.OnScrollListener {
         private final RecyclerView mRecyclerView;
 
         private int mOldScrollState;
@@ -322,7 +322,7 @@
         }
 
         @Override
-        public void onScrollStateChanged(int state) {
+        public void onScrollStateChanged(RecyclerView recyclerView, int state) {
             switch (mOldScrollState) {
                 case RecyclerView.SCROLL_STATE_SETTLING: {
                     if (state == RecyclerView.SCROLL_STATE_IDLE
@@ -341,11 +341,6 @@
             mOldScrollState = state;
         }
 
-        @Override
-        public void onScrolled(int dx, int dy) {
-            /* do nothing */
-        }
-
         public void startPreloadContent() {
             PageAdapter pageAdapter = (PageAdapter) mRecyclerView.getAdapter();
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5752646..a038899 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -109,6 +109,9 @@
 
     <uses-permission android:name="android.permission.CAMERA" />
 
+    <!-- Screen Capturing -->
+    <uses-permission android:name="android.permission.CREATE_MEDIA_PROJECTION" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
@@ -245,6 +248,15 @@
             android:taskAffinity="com.android.systemui.net"
             android:excludeFromRecents="true" />
 
+        <!-- started from MediaProjectionManager -->
+        <activity
+            android:name=".media.MediaProjectionPermissionActivity"
+            android:exported="true"
+            android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+            android:finishOnCloseSystemDialogs="true"
+            android:launchMode="singleTop"
+            android:excludeFromRecents="true" />
+
         <!-- platform logo easter egg activity -->
         <activity
             android:name=".DessertCase"
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6d101d7..c9e1618 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -9,4 +9,4 @@
 
 -keep class com.android.systemui.statusbar.phone.PhoneStatusBar
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
--keep class com.android.systemui.recents.*
\ No newline at end of file
+-keep class com.android.systemui.recents.*
diff --git a/packages/SystemUI/res/drawable/zen_alarm_hard_background.xml b/packages/SystemUI/res/drawable/zen_alarm_hard_background.xml
new file mode 100644
index 0000000..3620212
--- /dev/null
+++ b/packages/SystemUI/res/drawable/zen_alarm_hard_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/system_warning_color" />
+    <corners
+        android:topLeftRadius="0dp"
+        android:topRightRadius="0dp"
+        android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius"
+        android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/zen_alarm_soft_background.xml b/packages/SystemUI/res/drawable/zen_alarm_soft_background.xml
new file mode 100644
index 0000000..a99c79c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/zen_alarm_soft_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/zen_alarm_soft_warning_background" />
+    <corners
+        android:topLeftRadius="0dp"
+        android:topRightRadius="0dp"
+        android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius"
+        android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index 691a80e..d17390e 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -32,8 +32,8 @@
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
             />
     <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/picture"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
+            android:layout_width="@dimen/max_avatar_size"
+            android:layout_height="@dimen/max_avatar_size"
             android:contentDescription="@null"
             sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
             sysui:activeFrameColor="@color/current_user_border_color" />
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 29d92e5..526aa5e 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -29,8 +29,8 @@
 
     <com.android.systemui.statusbar.phone.UserAvatarView
             android:id="@+id/user_picture"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
+            android:layout_width="@dimen/max_avatar_size"
+            android:layout_height="@dimen/max_avatar_size"
             android:layout_marginBottom="12dp"
             systemui:frameWidth="2dp"
             systemui:activeFrameColor="@color/current_user_border_color"/>
diff --git a/packages/SystemUI/res/layout/remember_permission_checkbox.xml b/packages/SystemUI/res/layout/remember_permission_checkbox.xml
new file mode 100644
index 0000000..a21acb3
--- /dev/null
+++ b/packages/SystemUI/res/layout/remember_permission_checkbox.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Check box that is displayed in the activity resolver UI for the user
+     to make their selection the preferred activity. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="8dp"
+    android:paddingEnd="8dp"
+    android:paddingTop="8dp">
+
+    <CheckBox
+        android:id="@+id/remember"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:focusable="true"
+        android:clickable="true"
+        android:text="@string/media_projection_remember_text" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/segmented_button.xml b/packages/SystemUI/res/layout/segmented_button.xml
index ef78220..538e434 100644
--- a/packages/SystemUI/res/layout/segmented_button.xml
+++ b/packages/SystemUI/res/layout/segmented_button.xml
@@ -23,4 +23,4 @@
     android:background="@drawable/segmented_button"
     android:textAppearance="@style/TextAppearance.QS.SegmentedButton"
     android:minHeight="0dp"
-    android:padding="12dp" />
+    android:padding="10dp" />
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
index b377a06..6416308 100644
--- a/packages/SystemUI/res/layout/volume_panel.xml
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -18,6 +18,7 @@
     android:id="@+id/visible_panel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:paddingTop="8dp"
     android:orientation="vertical" >
 
     <FrameLayout
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
index d91e2ab..b1dd237 100644
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -26,10 +26,10 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:contentDescription="@null"
-        android:paddingBottom="12dip"
+        android:paddingBottom="10dip"
         android:paddingLeft="16dip"
         android:paddingRight="16dip"
-        android:paddingTop="12dip" />
+        android:paddingTop="10dip" />
 
     <SeekBar
         android:id="@+id/seekbar"
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 8b1c2b7..b0e43fc 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -30,7 +30,7 @@
         android:layout_height="wrap_content"
         android:layout_marginLeft="@dimen/qs_panel_padding"
         android:layout_marginRight="@dimen/qs_panel_padding"
-        android:layout_marginTop="8dp"
+        android:layout_marginTop="12dp"
         android:background="@drawable/segmented_buttons"
         android:clipChildren="false" />
 
@@ -81,8 +81,10 @@
     <TextView
         android:id="@+id/zen_alarm_warning"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/qs_panel_padding"
-        android:gravity="center"
-        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+        android:layout_height="48dp"
+        android:layout_marginTop="@dimen/qs_panel_padding"
+        android:paddingLeft="@dimen/qs_panel_padding"
+        android:paddingRight="@dimen/qs_panel_padding"
+        android:gravity="center_vertical"
+        android:textAppearance="@style/TextAppearance.QS.DetailEmpty" />
 </com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a1064fd..f23486e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -43,6 +43,8 @@
     <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
     <color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
     <color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200-->
+    <color name="zen_alarm_soft_warning_text">#99FFFFFF</color><!-- 60% white -->
+    <color name="zen_alarm_soft_warning_background">#15FFFFFF</color><!-- 8% white -->
     <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
     <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
     <color name="data_usage_graph_warning">#FFFFFFFF</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e86aa0a..bb18120 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -361,4 +361,8 @@
 
     <!-- Battery level padding end when in expanded QS (but not on Keyguard) -->
     <dimen name="battery_level_padding_end">4dp</dimen>
+
+    <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
+         quick settings header -->
+    <dimen name="max_avatar_size">48dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f8c9d4c..46057af 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -703,5 +703,15 @@
 
     <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
     <string name="notification_hidden_text">Contents hidden</string>
+
     <string name="guest_exit_guest">Exit guest</string>
+
+    <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
+    <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string>
+
+    <!-- Media projection permission dialog permanent grant check box. [CHAR LIMIT=NONE] -->
+    <string name="media_projection_remember_text">Don\'t show again</string>
+
+    <!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
+    <string name="media_projection_action_text">Start now</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5cc987a..419ecc7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -267,8 +267,8 @@
 
     <!-- Window animations used for volume panel. -->
     <style name="VolumePanelAnimation">
-        <item name="android:windowEnterAnimation">@*android:anim/dock_top_enter</item>
-        <item name="android:windowExitAnimation">@*android:anim/dock_top_exit</item>
+        <item name="android:windowEnterAnimation">@*android:anim/popup_enter_material</item>
+        <item name="android:windowExitAnimation">@*android:anim/popup_exit_material</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Material.EventContent.Parenthetical"
diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
new file mode 100644
index 0000000..008b422
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.systemui;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+
+public class BitmapHelper {
+    /**
+     * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
+     * to fit and clipped to an inscribed circle.
+     * @param input Bitmap to resize and clip
+     * @param width Width of output bitmap (and diameter of circle)
+     * @param height Height of output bitmap
+     * @return A shiny new bitmap for you to use
+     */
+    public static Bitmap createCircularClip(Bitmap input, int width, int height) {
+        final int inWidth = input.getWidth();
+        final int inHeight = input.getHeight();
+        final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+        paint.setAntiAlias(true);
+        final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
+        final RectF dstRect = new RectF(0, 0, width, height);
+        final Matrix m = new Matrix();
+        m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
+        canvas.setMatrix(m);
+        canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
+        return output;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
new file mode 100644
index 0000000..b441eaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -0,0 +1,138 @@
+/*
+ * 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.systemui.media;
+
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.media.projection.MediaProjectionManager;
+import android.media.projection.IMediaProjectionManager;
+import android.media.projection.IMediaProjection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+public class MediaProjectionPermissionActivity extends AlertActivity
+        implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
+    private static final String TAG = "MediaProjectionPermissionActivity";
+
+    private boolean mPermanentGrant;
+    private String mPackageName;
+    private int mUid;
+    private IMediaProjectionManager mService;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        Intent intent = getIntent();
+        mPackageName = getCallingPackage();
+        IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
+        mService = IMediaProjectionManager.Stub.asInterface(b);
+
+        if (mPackageName == null) {
+            finish();
+            return;
+        }
+
+        PackageManager packageManager = getPackageManager();
+        ApplicationInfo aInfo;
+        try {
+            aInfo = packageManager.getApplicationInfo(mPackageName, 0);
+            mUid = aInfo.uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "unable to look up package name", e);
+            finish();
+            return;
+        }
+
+        try {
+            if (mService.hasProjectionPermission(mUid, mPackageName)) {
+                setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName,
+                        false /*permanentGrant*/));
+                finish();
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error checking projection permissions", e);
+            finish();
+            return;
+        }
+
+        String appName = aInfo.loadLabel(packageManager).toString();
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mIcon = aInfo.loadIcon(packageManager);
+        ap.mMessage = getString(R.string.media_projection_dialog_text, appName);
+        ap.mPositiveButtonText = getString(R.string.media_projection_action_text);
+        ap.mNegativeButtonText = getString(android.R.string.cancel);
+        ap.mPositiveButtonListener = this;
+        ap.mNegativeButtonListener = this;
+
+        // add "always use" checkbox
+        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        ap.mView = inflater.inflate(R.layout.remember_permission_checkbox, null);
+        CheckBox rememberPermissionCheckbox =
+                (CheckBox)ap.mView.findViewById(R.id.remember);
+        rememberPermissionCheckbox.setOnCheckedChangeListener(this);
+
+        setupAlert();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        try {
+            if (which == AlertDialog.BUTTON_POSITIVE) {
+                setResult(RESULT_OK, getMediaProjectionIntent(
+                        mUid, mPackageName, mPermanentGrant));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error granting projection permission", e);
+            setResult(RESULT_CANCELED);
+        } finally {
+            finish();
+        }
+    }
+
+    @Override
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        mPermanentGrant = isChecked;
+    }
+
+    private Intent getMediaProjectionIntent(int uid, String packageName, boolean permanentGrant)
+            throws RemoteException {
+        IMediaProjection projection = mService.createProjection(uid, packageName,
+                 MediaProjectionManager.TYPE_SCREEN_CAPTURE, permanentGrant);
+        Intent intent = new Intent();
+        intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder());
+        return intent;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index bd4ea90..068cbfe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -378,6 +378,8 @@
         }
         // Re-enable clipping with the stack (we will reuse this view)
         tv.setClipViewInStack(true);
+        // Re-enable touch events from this task view
+        tv.setTouchEnabled(true);
         // Remove the task view from the stack
         mSv.onTaskViewDismissed(tv);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 72c12d6..e975045 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1240,22 +1240,25 @@
 
             if (!notificationIsForCurrentProfiles(ent.notification)) continue;
 
+            final boolean hideSensitive = shouldHideSensitiveContents(ent.notification.getUserId());
             final int vis = ent.notification.getNotification().visibility;
-            if (vis != Notification.VISIBILITY_SECRET) {
-                // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications
-                boolean showingPublic = isLockscreenPublicMode()
-                        && vis == Notification.VISIBILITY_PRIVATE
-                        && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
-                ent.row.setShowingPublic(showingPublic);
-                if (ent.autoRedacted && ent.legacy) {
-                    if (showingPublic) {
-                        ent.row.setShowingLegacyBackground(false);
-                    } else {
-                        ent.row.setShowingLegacyBackground(true);
-                    }
-                }
-                toShow.add(ent.row);
+
+            // when isLockscreenPublicMode() we suppress VISIBILITY_SECRET notifications
+            if (vis == Notification.VISIBILITY_SECRET && hideSensitive) {
+                continue;
             }
+
+            // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications
+            boolean showingPublic = vis == Notification.VISIBILITY_PRIVATE && hideSensitive;
+            ent.row.setShowingPublic(showingPublic);
+            if (ent.autoRedacted && ent.legacy) {
+                if (showingPublic) {
+                    ent.row.setShowingLegacyBackground(false);
+                } else {
+                    ent.row.setShowingLegacyBackground(true);
+                }
+            }
+            toShow.add(ent.row);
         }
 
         ArrayList<View> toRemove = new ArrayList<View>();
@@ -1333,6 +1336,15 @@
         updateNotificationIcons();
     }
 
+    /**
+     * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
+     * notification data. If so, private notifications should show their (possibly
+     * auto-generated) publicVersion, and secret notifications should be totally invisible.
+     */
+    private boolean shouldHideSensitiveContents(int userid) {
+        return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
+    }
+
     private void updateNotificationIcons() {
         final LinearLayout.LayoutParams params
             = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
@@ -1353,10 +1365,8 @@
             if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
                     || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
             if (!notificationIsForCurrentProfiles(ent.notification)) continue;
-            if (isLockscreenPublicMode()
-                    && ent.notification.getNotification().visibility
-                            == Notification.VISIBILITY_SECRET
-                    && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())) {
+            if (ent.notification.getNotification().visibility == Notification.VISIBILITY_SECRET
+                    && shouldHideSensitiveContents(ent.notification.getUserId())) {
                 // in "public" mode (atop a secure keyguard), secret notifs are totally hidden
                 continue;
             }
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 574d536..3012b13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -285,6 +285,9 @@
         mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
         mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
         mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        if (mStatusIcons != null) {
+            mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
+        }
         if (mSignalCluster != null) {
             mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
         }
@@ -437,15 +440,16 @@
     public void attachSystemIcons(LinearLayout systemIcons) {
         mSystemIconsContainer.addView(systemIcons);
         mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
-        mStatusIcons.addOnLayoutChangeListener(mStatusIconsChanged);
         mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
+        mSignalCluster.addOnLayoutChangeListener(mSignalClusterChanged);
     }
 
     public void onSystemIconsDetached() {
         if (mStatusIcons != null) {
-            mStatusIcons.removeOnLayoutChangeListener(mStatusIconsChanged);
+            mStatusIcons.setVisibility(View.VISIBLE);
         }
         if (mSignalCluster != null) {
+            mSignalCluster.removeOnLayoutChangeListener(mSignalClusterChanged);
             mSignalCluster.setVisibility(View.VISIBLE);
         }
         mStatusIcons = null;
@@ -528,7 +532,7 @@
         // here.
     }
 
-    private final OnLayoutChangeListener mStatusIconsChanged = new OnLayoutChangeListener() {
+    private final OnLayoutChangeListener mSignalClusterChanged = new OnLayoutChangeListener() {
         private final Rect mClipBounds = new Rect();
 
         @Override
@@ -536,12 +540,9 @@
                 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
             // Hide the statusIcons in the header by clipping them.  Can't touch visibility since
             // they are mirrored to the real status bar.
-            final Rect r = mSystemIconsContainer.getClipBounds();
-            if (r == null || r.left != right) {
-                mClipBounds.set(right, 0, mSystemIconsContainer.getWidth(),
-                        mSystemIconsContainer.getHeight());
-                mSystemIconsContainer.setClipBounds(mClipBounds);
-            }
+            mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
+                    mSystemIconsContainer.getHeight());
+            mSystemIconsContainer.setClipBounds(mClipBounds);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index c90750c..a3f3819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.BitmapHelper;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.StatusBarHeaderView;
 import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -176,11 +177,16 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Couln't get current user.", e);
         }
+        final int avatarSize
+                = mContext.getResources().getDimensionPixelSize(R.dimen.max_avatar_size);
         for (int i = 0; i < N; i++) {
             UserInfo user = users.get(i);
             if (user.supportsSwitchTo()) {
                 boolean isCurrent = user.id == currentUser;
-                result.add(new UserData(user, mUserManager.getUserIcon(user.id), isCurrent));
+                final Bitmap picture = BitmapHelper.createCircularClip(
+                        mUserManager.getUserIcon(user.id),
+                        avatarSize, avatarSize);
+                result.add(new UserData(user, picture, isCurrent));
             }
         }
         return result;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 3ce6905..8cbe272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -28,10 +28,12 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Shader;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -46,6 +48,7 @@
 import android.util.Pair;
 
 import com.android.internal.view.RotationPolicy;
+import com.android.systemui.BitmapHelper;
 import com.android.systemui.R;
 
 import java.util.ArrayList;
@@ -124,17 +127,6 @@
         queryForUserInformation();
     }
 
-    private Bitmap circularClip(Bitmap input) {
-        Bitmap output = Bitmap.createBitmap(input.getWidth(),
-                input.getHeight(), Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        paint.setAntiAlias(true);
-        canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, paint);
-        return output;
-    }
-
     private void queryForUserInformation() {
         Context currentUserContext;
         UserInfo userInfo;
@@ -151,6 +143,8 @@
         }
         final int userId = userInfo.id;
         final String userName = userInfo.name;
+        final int avatarSize
+                = mContext.getResources().getDimensionPixelSize(R.dimen.max_avatar_size);
 
         final Context context = currentUserContext;
         mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
@@ -164,7 +158,8 @@
                 Drawable avatar = null;
                 Bitmap rawAvatar = um.getUserIcon(userId);
                 if (rawAvatar != null) {
-                    avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
+                    avatar = new BitmapDrawable(mContext.getResources(),
+                            BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize));
                 } else {
                     avatar = mContext.getResources().getDrawable(R.drawable.ic_account_circle);
                     mUseDefaultAvatar = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 7cc8ed5..2134042 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.BitmapHelper;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.UserDetailView;
@@ -104,6 +105,8 @@
                 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
                 int currentId = ActivityManager.getCurrentUser();
                 UserRecord guestRecord = null;
+                int avatarSize = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.max_avatar_size);
 
                 for (UserInfo info : infos) {
                     boolean isCurrent = currentId == info.id;
@@ -115,6 +118,10 @@
                         if (picture == null) {
                             picture = mUserManager.getUserIcon(info.id);
                         }
+                        if (picture != null) {
+                            picture = BitmapHelper.createCircularClip(
+                                    picture, avatarSize, avatarSize);
+                        }
                         records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent));
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e38c2ac..6d6fd60 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -132,6 +132,9 @@
                     final Intent intent = ZenModePanel.ZEN_SETTINGS;
                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                     mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+
+                    // dismiss shade if showing
+                    mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
                 }
             });
             mDialogPanel.postDismiss(mDismissDelay);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 33cf3b6..7473c41 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -80,8 +81,8 @@
     private final H mHandler = new H();
     private final Favorites mFavorites;
     private final Interpolator mFastOutSlowInInterpolator;
-    private final int mTextColor;
-    private final int mAccentColor;
+    private final int mHardWarningColor;
+    private final int mSoftWarningColor;
 
     private char mLogTag = '?';
     private String mTag;
@@ -94,6 +95,7 @@
     private LinearLayout mZenConditions;
     private TextView mAlarmWarning;
 
+    private int mBottomPadding;
     private Callback mCallback;
     private ZenModeController mController;
     private boolean mRequestingConditions;
@@ -112,8 +114,9 @@
         mInflater = LayoutInflater.from(mContext.getApplicationContext());
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
                 android.R.interpolator.fast_out_slow_in);
-        mTextColor = mContext.getResources().getColor(R.color.qs_text);
-        mAccentColor = mContext.getResources().getColor(R.color.system_accent_color);
+        final Resources res = mContext.getResources();
+        mHardWarningColor = res.getColor(R.color.qs_text);
+        mSoftWarningColor = res.getColor(R.color.zen_alarm_soft_warning_text);
         updateTag();
         if (DEBUG) Log.d(mTag, "new ZenModePanel");
     }
@@ -126,6 +129,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mBottomPadding = getPaddingBottom();
+
         mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
         mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
         mZenButtons.addButton(R.string.interruption_level_priority,
@@ -285,6 +290,7 @@
         final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
         final boolean foreverSelected = mExitConditionId == null;
         final boolean hasNextAlarm = mNextAlarm != 0;
+        final boolean showAlarmWarning = zenNone && mExpanded && hasNextAlarm;
 
         mZenSubhead.setVisibility(!zenOff && (mExpanded || !foreverSelected) ? VISIBLE : GONE);
         mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
@@ -292,8 +298,8 @@
         mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
         mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
         mAlarmWarning.setVisibility(zenNone && mExpanded && hasNextAlarm ? VISIBLE : GONE);
-
-        if (zenNone && mExpanded && hasNextAlarm) {
+        setPadding(0, 0, 0, showAlarmWarning ? 0 : mBottomPadding);
+        if (showAlarmWarning) {
             final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId);
             final long now = System.currentTimeMillis();
             final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay;
@@ -304,12 +310,14 @@
             final boolean isWarning = exitTime > 0 && mNextAlarm > now && mNextAlarm < exitTime;
             if (isWarning) {
                 mAlarmWarning.setText(mContext.getString(R.string.zen_alarm_warning, alarm));
-                mAlarmWarning.setTextColor(mAccentColor);
+                mAlarmWarning.setTextColor(mHardWarningColor);
+                mAlarmWarning.setBackgroundResource(R.drawable.zen_alarm_hard_background);
             } else {
                 mAlarmWarning.setText(mContext.getString(alarmToday
                         ? R.string.zen_alarm_information_time
                         : R.string.zen_alarm_information_day_time, alarm));
-                mAlarmWarning.setTextColor(mTextColor);
+                mAlarmWarning.setTextColor(mSoftWarningColor);
+                mAlarmWarning.setBackgroundResource(R.drawable.zen_alarm_soft_background);
             }
         }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index da0bc30..1e61236 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -71,6 +71,7 @@
 import android.util.TypedValue;
 import android.view.ActionMode;
 import android.view.ContextThemeWrapper;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.IRotationWatcher;
 import android.view.IWindowManager;
@@ -3182,9 +3183,17 @@
         if (a.getBoolean(com.android.internal.R.styleable.Window_windowContentTransitions, false)) {
             requestFeature(FEATURE_CONTENT_TRANSITIONS);
         }
-        if (a.hasValue(com.android.internal.R.styleable.Window_windowOutsetBottom)) {
-            if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
-            a.getValue(com.android.internal.R.styleable.Window_windowOutsetBottom, mOutsetBottom);
+
+        final WindowManager windowService = (WindowManager) getContext().getSystemService(
+                Context.WINDOW_SERVICE);
+        if (windowService != null) {
+            final Display display = windowService.getDefaultDisplay();
+            if (display.getDisplayId() == Display.DEFAULT_DISPLAY &&
+                    a.hasValue(com.android.internal.R.styleable.Window_windowOutsetBottom)) {
+                if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
+                a.getValue(com.android.internal.R.styleable.Window_windowOutsetBottom,
+                        mOutsetBottom);
+            }
         }
 
         final Context context = getContext();
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f54f798..17a5263 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3539,6 +3539,11 @@
                             // In all cases we need to give the transport its finish callback
                             int finishResult = transport.finishBackup();
 
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "Done trying to send backup data: result="
+                                        + result + " finishResult=" + finishResult);
+                            }
+
                             // If we were otherwise in a good state, now interpret the final
                             // result based on what finishBackup() returned.  If we're in a
                             // failure case already, preserve that result and ignore whatever
@@ -3561,7 +3566,7 @@
                             // do nothing, clean up, and continue looping
                         } else if (result != BackupTransport.TRANSPORT_OK) {
                             if (DEBUG) {
-                                Slog.i(TAG, "Transport failed; aborting backup");
+                                Slog.i(TAG, "Transport failed; aborting backup: " + result);
                                 return;
                             }
                         }
@@ -4083,6 +4088,9 @@
                             // okay, if the remote end failed at any point, deal with
                             // it by ignoring the rest of the restore on it
                             if (!agentSuccess) {
+                                if (DEBUG) {
+                                    Slog.i(TAG, "Agent failure; ending restore");
+                                }
                                 mBackupHandler.removeMessages(MSG_TIMEOUT);
                                 tearDownPipes();
                                 tearDownAgent(mTargetApp);
@@ -4124,6 +4132,11 @@
 
             // If we got here we're either running smoothly or we've finished
             if (info == null) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "No [more] data for this package; tearing down");
+                }
+                tearDownPipes();
+                tearDownAgent(mTargetApp);
                 setRunning(false);
             }
             return (info != null);
@@ -7037,6 +7050,7 @@
                             // handling will deal properly with that.
                             Slog.e(TAG, "Error " + result + " streaming restore for "
                                     + mCurrentPackage.packageName);
+                            status = result;
                         }
                     }
                     if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
@@ -7063,6 +7077,10 @@
                     // Don't proceed until the engine has torn down the agent etc
                     eThread.waitForResult();
 
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "engine thread finished; proceeding");
+                    }
+
                     // Now we're really done with this one too
                     IoUtils.closeQuietly(mEnginePipes[0]);
 
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 1eded6f..f71a18a 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.server;
 
 import android.Manifest;
@@ -8,8 +24,9 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.service.persistentdata.IPersistentDataBlockService;
-import android.util.Log;
+import android.util.Slog;
 import com.android.internal.R;
+import libcore.io.IoUtils;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -23,7 +40,8 @@
 
 /**
  * Service for reading and writing blocks to a persistent partition.
- * This data will live across factory resets.
+ * This data will live across factory resets not initiated via the Settings UI.
+ * When a device is factory reset through Settings this data is wiped.
  *
  * Allows writing one block at a time. Namely, each time
  * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data)
@@ -40,19 +58,29 @@
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
     private static final int HEADER_SIZE = 8;
-    private static final int BLOCK_ID = 0x1990;
+    // Magic number to mark block device as adhering to the format consumed by this service
+    private static final int PARTITION_TYPE_MARKER = 0x1990;
+    // Limit to 100k as blocks larger than this might cause strain on Binder.
+    // TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager
+    private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
 
     private final Context mContext;
     private final String mDataBlockFile;
-    private long mBlockDeviceSize;
-
     private final int mAllowedUid;
+    private final Object mLock = new Object();
+    /*
+     * Separate lock for OEM unlock related operations as they can happen in parallel with regular
+     * block operations.
+     */
+    private final Object mOemLock = new Object();
+
+    private long mBlockDeviceSize;
 
     public PersistentDataBlockService(Context context) {
         super(context);
         mContext = context;
         mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
-        mBlockDeviceSize = 0; // Load lazily
+        mBlockDeviceSize = -1; // Load lazily
         String allowedPackage = context.getResources()
                 .getString(R.string.config_persistentDataPackageName);
         PackageManager pm = mContext.getPackageManager();
@@ -62,7 +90,7 @@
                     Binder.getCallingUserHandle().getIdentifier());
         } catch (PackageManager.NameNotFoundException e) {
             // not expected
-            Log.e(TAG, "not able to find package " + allowedPackage, e);
+            Slog.e(TAG, "not able to find package " + allowedPackage, e);
         }
 
         mAllowedUid = allowedUid;
@@ -85,10 +113,10 @@
         }
     }
 
-    private int getTotalDataSize(DataInputStream inputStream) throws IOException {
+    private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
         int totalDataSize;
         int blockId = inputStream.readInt();
-        if (blockId == BLOCK_ID) {
+        if (blockId == PARTITION_TYPE_MARKER) {
             totalDataSize = inputStream.readInt();
         } else {
             totalDataSize = 0;
@@ -96,17 +124,18 @@
         return totalDataSize;
     }
 
-    private long maybeReadBlockDeviceSize() {
-        synchronized (this) {
-            if (mBlockDeviceSize == 0) {
-                mBlockDeviceSize = getBlockDeviceSize(mDataBlockFile);
+    private long getBlockDeviceSize() {
+        synchronized (mLock) {
+            if (mBlockDeviceSize == -1) {
+                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
             }
         }
 
         return mBlockDeviceSize;
     }
 
-    private native long getBlockDeviceSize(String path);
+    private native long nativeGetBlockDeviceSize(String path);
+    private native int nativeWipe(String path);
 
     private final IBinder mService = new IPersistentDataBlockService.Stub() {
         @Override
@@ -114,62 +143,93 @@
             enforceUid(Binder.getCallingUid());
 
             // Need to ensure we don't write over the last byte
-            if (data.length > maybeReadBlockDeviceSize() - HEADER_SIZE - 1) {
-                return -1;
+            long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
+            if (data.length > maxBlockSize) {
+                // partition is ~500k so shouldn't be a problem to downcast
+                return (int) -maxBlockSize;
             }
 
             DataOutputStream outputStream;
             try {
                 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
             } catch (FileNotFoundException e) {
-                Log.e(TAG, "partition not available?", e);
+                Slog.e(TAG, "partition not available?", e);
                 return -1;
             }
 
             ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
-            headerAndData.putInt(BLOCK_ID);
+            headerAndData.putInt(PARTITION_TYPE_MARKER);
             headerAndData.putInt(data.length);
             headerAndData.put(data);
 
             try {
-                outputStream.write(headerAndData.array());
-                return data.length;
+                synchronized (mLock) {
+                    outputStream.write(headerAndData.array());
+                    return data.length;
+                }
             } catch (IOException e) {
-                Log.e(TAG, "failed writing to the persistent data block", e);
+                Slog.e(TAG, "failed writing to the persistent data block", e);
                 return -1;
             } finally {
                 try {
                     outputStream.close();
                 } catch (IOException e) {
-                    Log.e(TAG, "failed closing output stream", e);
+                    Slog.e(TAG, "failed closing output stream", e);
                 }
             }
         }
 
         @Override
-        public int read(byte[] data) {
+        public byte[] read() {
             enforceUid(Binder.getCallingUid());
 
             DataInputStream inputStream;
             try {
                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
             } catch (FileNotFoundException e) {
-                Log.e(TAG, "partition not available?", e);
-                return -1;
+                Slog.e(TAG, "partition not available?", e);
+                return null;
             }
 
             try {
-                int totalDataSize = getTotalDataSize(inputStream);
-                return inputStream.read(data, 0,
-                        (data.length > totalDataSize) ? totalDataSize : data.length);
+                synchronized (mLock) {
+                    int totalDataSize = getTotalDataSizeLocked(inputStream);
+
+                    if (totalDataSize == 0) {
+                        return new byte[0];
+                    }
+
+                    byte[] data = new byte[totalDataSize];
+                    int read = inputStream.read(data, 0, totalDataSize);
+                    if (read < totalDataSize) {
+                        // something went wrong, not returning potentially corrupt data
+                        Slog.e(TAG, "failed to read entire data block. bytes read: " +
+                                read + "/" + totalDataSize);
+                        return null;
+                    }
+                    return data;
+                }
             } catch (IOException e) {
-                Log.e(TAG, "failed to read data", e);
-                return -1;
+                Slog.e(TAG, "failed to read data", e);
+                return null;
             } finally {
                 try {
                     inputStream.close();
                 } catch (IOException e) {
-                    Log.e(TAG, "failed to close OutputStream");
+                    Slog.e(TAG, "failed to close OutputStream");
+                }
+            }
+        }
+
+        @Override
+        public void wipe() {
+            enforceOemUnlockPermission();
+
+            synchronized (mLock) {
+                int ret = nativeWipe(mDataBlockFile);
+
+                if (ret < 0) {
+                    Slog.e(TAG, "failed to wipe persistent partition");
                 }
             }
         }
@@ -181,28 +241,26 @@
             try {
                 outputStream = new FileOutputStream(new File(mDataBlockFile));
             } catch (FileNotFoundException e) {
-                Log.e(TAG, "parition not available", e);
+                Slog.e(TAG, "parition not available", e);
                 return;
             }
 
             try {
                 FileChannel channel = outputStream.getChannel();
 
-                channel.position(maybeReadBlockDeviceSize() - 1);
+                channel.position(getBlockDeviceSize() - 1);
 
                 ByteBuffer data = ByteBuffer.allocate(1);
                 data.put(enabled ? (byte) 1 : (byte) 0);
                 data.flip();
 
-                channel.write(data);
-            } catch (IOException e) {
-                Log.e(TAG, "unable to access persistent partition", e);
-            } finally {
-                try {
-                    outputStream.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to close OutputStream");
+                synchronized (mOemLock) {
+                    channel.write(data);
                 }
+            } catch (IOException e) {
+                Slog.e(TAG, "unable to access persistent partition", e);
+            } finally {
+                IoUtils.closeQuietly(outputStream);
             }
         }
 
@@ -213,22 +271,20 @@
             try {
                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
             } catch (FileNotFoundException e) {
-                Log.e(TAG, "partition not available");
+                Slog.e(TAG, "partition not available");
                 return false;
             }
 
             try {
-                inputStream.skip(maybeReadBlockDeviceSize() - 1);
-                return inputStream.readByte() != 0;
+                inputStream.skip(getBlockDeviceSize() - 1);
+                synchronized (mOemLock) {
+                    return inputStream.readByte() != 0;
+                }
             } catch (IOException e) {
-                Log.e(TAG, "unable to access persistent partition", e);
+                Slog.e(TAG, "unable to access persistent partition", e);
                 return false;
             } finally {
-                try {
-                    inputStream.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to close OutputStream");
-                }
+                IoUtils.closeQuietly(inputStream);
             }
         }
 
@@ -240,22 +296,27 @@
             try {
                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
             } catch (FileNotFoundException e) {
-                Log.e(TAG, "partition not available");
+                Slog.e(TAG, "partition not available");
                 return 0;
             }
 
             try {
-                return getTotalDataSize(inputStream);
+                synchronized (mLock) {
+                    return getTotalDataSizeLocked(inputStream);
+                }
             } catch (IOException e) {
-                Log.e(TAG, "error reading data block size");
+                Slog.e(TAG, "error reading data block size");
                 return 0;
             } finally {
-                try {
-                    inputStream.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "failed to close OutputStream");
-                }
+                IoUtils.closeQuietly(inputStream);
             }
         }
+
+        @Override
+        public long getMaximumDataBlockSize() {
+            long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
+            return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
+        }
+
     };
 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4cfd042..9d1f8c6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3757,9 +3757,10 @@
 
         VirtualActivityDisplay(int width, int height, int density) {
             DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, VIRTUAL_DISPLAY_BASE_NAME,
-                    width, height, density, null, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
-                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+            mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, null,
+                    VIRTUAL_DISPLAY_BASE_NAME, width, height, density, null,
+                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY, null, null);
 
             init(mVirtualDisplay.getDisplay());
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6697b60..2737646 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -29,8 +29,11 @@
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
 import android.hardware.display.IDisplayManager;
 import android.hardware.display.IDisplayManagerCallback;
+import android.hardware.display.IVirtualDisplayCallbacks;
 import android.hardware.display.WifiDisplayStatus;
 import android.hardware.input.InputManagerInternal;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,6 +42,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.text.TextUtils;
@@ -127,6 +131,7 @@
     private final DisplayAdapterListener mDisplayAdapterListener;
     private WindowManagerInternal mWindowManagerInternal;
     private InputManagerInternal mInputManagerInternal;
+    private IMediaProjectionManager mProjectionService;
 
     // The synchronization root for the display manager.
     // This lock guards most of the display manager's state.
@@ -486,7 +491,8 @@
         }
     }
 
-    private int createVirtualDisplayInternal(IBinder appToken, int callingUid, String packageName,
+    private int createVirtualDisplayInternal(IVirtualDisplayCallbacks callbacks,
+            IMediaProjection projection, int callingUid, String packageName,
             String name, int width, int height, int densityDpi, Surface surface, int flags) {
         synchronized (mSyncRoot) {
             if (mVirtualDisplayAdapter == null) {
@@ -496,8 +502,8 @@
             }
 
             DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
-                    appToken, callingUid, packageName, name, width, height, densityDpi,
-                    surface, flags);
+                    callbacks, projection, callingUid, packageName,
+                    name, width, height, densityDpi, surface, flags);
             if (device == null) {
                 return -1;
             }
@@ -511,7 +517,7 @@
             // Something weird happened and the logical display was not created.
             Slog.w(TAG, "Rejecting request to create virtual display "
                     + "because the logical display was not created.");
-            mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
+            mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callbacks.asBinder());
             handleDisplayDeviceRemovedLocked(device);
         }
         return -1;
@@ -878,6 +884,14 @@
         mTempCallbacks.clear();
     }
 
+    private IMediaProjectionManager getProjectionService() {
+        if (mProjectionService == null) {
+            IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
+            mProjectionService = IMediaProjectionManager.Stub.asInterface(b);
+        }
+        return mProjectionService;
+    }
+
     private void dumpInternal(PrintWriter pw) {
         pw.println("DISPLAY MANAGER (dumpsys display)");
 
@@ -1215,13 +1229,14 @@
         }
 
         @Override // Binder call
-        public int createVirtualDisplay(IBinder appToken, String packageName,
-                String name, int width, int height, int densityDpi, Surface surface, int flags) {
+        public int createVirtualDisplay(IVirtualDisplayCallbacks callbacks,
+                IMediaProjection projection, String packageName, String name,
+                int width, int height, int densityDpi, Surface surface, int flags) {
             final int callingUid = Binder.getCallingUid();
             if (!validatePackageName(callingUid, packageName)) {
                 throw new SecurityException("packageName must match the calling uid");
             }
-            if (appToken == null) {
+            if (callbacks == null) {
                 throw new IllegalArgumentException("appToken must not be null");
             }
             if (TextUtils.isEmpty(name)) {
@@ -1231,51 +1246,78 @@
                 throw new IllegalArgumentException("width, height, and densityDpi must be "
                         + "greater than 0");
             }
+
+            if (projection != null) {
+                try {
+                    if (!getProjectionService().isValidMediaProjection(projection)) {
+                        throw new SecurityException("Invalid media projection");
+                    }
+                } catch (RemoteException e) {
+                    throw new SecurityException("unable to validate media projection");
+                }
+                flags &= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+                try {
+                    flags |= projection.getVirtualDisplayFlags();
+                } catch (RemoteException e) {
+                    throw new RuntimeException("unable to retrieve media projection flags");
+                }
+            }
+
+            if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE) != 0) {
+                if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0 ||
+                        (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
+                    throw new IllegalArgumentException("screen sharing virtual displays must not "
+                            + "be public or presentation displays");
+                }
+                if (!canProjectVideo(projection)) {
+                    throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
+                            + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
+                            + "MediaProjection token in order to create a screen sharing virtual "
+                            + "display.");
+                }
+            }
+
+
             if (callingUid != Process.SYSTEM_UID &&
                     (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
-                if (mContext.checkCallingPermission(android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
-                        != PackageManager.PERMISSION_GRANTED
-                        && mContext.checkCallingPermission(
-                                android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT)
-                                != PackageManager.PERMISSION_GRANTED) {
+                if (!canProjectVideo(projection)) {
                     throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
-                            + "CAPTURE_SECURE_VIDEO_OUTPUT permission to create a "
-                            + "public virtual display.");
+                            + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
+                            + "MediaProjection token to create a public virtual display.");
                 }
             }
             if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
-                if (mContext.checkCallingPermission(
-                        android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT)
-                        != PackageManager.PERMISSION_GRANTED) {
+                if (!canProjectSecureVideo(projection)) {
                     throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
-                            + "to create a secure virtual display.");
+                            + "or an appropriate MediaProjection token to create a "
+                            + "secure virtual display.");
                 }
             }
 
             final long token = Binder.clearCallingIdentity();
             try {
-                return createVirtualDisplayInternal(appToken, callingUid, packageName,
-                        name, width, height, densityDpi, surface, flags);
+                return createVirtualDisplayInternal(callbacks, projection, callingUid,
+                        packageName, name, width, height, densityDpi, surface, flags);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override // Binder call
-        public void setVirtualDisplaySurface(IBinder appToken, Surface surface) {
+        public void setVirtualDisplaySurface(IVirtualDisplayCallbacks callbacks, Surface surface) {
             final long token = Binder.clearCallingIdentity();
             try {
-                setVirtualDisplaySurfaceInternal(appToken, surface);
+                setVirtualDisplaySurfaceInternal(callbacks.asBinder(), surface);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override // Binder call
-        public void releaseVirtualDisplay(IBinder appToken) {
+        public void releaseVirtualDisplay(IVirtualDisplayCallbacks callbacks) {
             final long token = Binder.clearCallingIdentity();
             try {
-                releaseVirtualDisplayInternal(appToken);
+                releaseVirtualDisplayInternal(callbacks.asBinder());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1312,6 +1354,39 @@
             }
             return false;
         }
+
+        private boolean canProjectVideo(IMediaProjection projection) {
+            if (projection != null) {
+                try {
+                    if (projection.canProjectVideo()) {
+                        return true;
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Unable to query projection service for permissions", e);
+                }
+            }
+            if (mContext.checkCallingPermission(
+                    android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+            return canProjectSecureVideo(projection);
+        }
+
+        private boolean canProjectSecureVideo(IMediaProjection projection) {
+            if (projection != null) {
+                try {
+                    if (projection.canProjectSecureVideo()){
+                        return true;
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Unable to query projection service for permissions", e);
+                }
+            }
+            return mContext.checkCallingPermission(
+                    android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT)
+                    != PackageManager.PERMISSION_GRANTED;
+        }
     }
 
     private final class LocalService extends DisplayManagerInternal {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index ed619d9..284780d 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -337,4 +337,4 @@
         pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
         pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index ec14cf1..1032081 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -18,9 +18,13 @@
 
 import android.content.Context;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.IVirtualDisplayCallbacks;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionCallback;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -28,6 +32,8 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import java.io.PrintWriter;
+
 /**
  * A display adapter that provides virtual displays on behalf of applications.
  * <p>
@@ -40,30 +46,38 @@
 
     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
             new ArrayMap<IBinder, VirtualDisplayDevice>();
+    private Handler mHandler;
 
     // Called with SyncRoot lock held.
     public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
         super(syncRoot, context, handler, listener, TAG);
+        mHandler = handler;
     }
 
-    public DisplayDevice createVirtualDisplayLocked(IBinder appToken,
-            int ownerUid, String ownerPackageName,
+    public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallbacks callbacks,
+            IMediaProjection projection, int ownerUid, String ownerPackageName,
             String name, int width, int height, int densityDpi, Surface surface, int flags) {
         boolean secure = (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
+        IBinder appToken = callbacks.asBinder();
         IBinder displayToken = SurfaceControl.createDisplay(name, secure);
         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
-                ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags);
+                ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags,
+                new Callbacks(callbacks, mHandler));
+
+        mVirtualDisplayDevices.put(appToken, device);
 
         try {
+            if (projection != null) {
+                projection.addCallback(new MediaProjectionCallback(appToken));
+            }
             appToken.linkToDeath(device, 0);
         } catch (RemoteException ex) {
+            mVirtualDisplayDevices.remove(appToken);
             device.destroyLocked();
             return null;
         }
 
-        mVirtualDisplayDevices.put(appToken, device);
-
         // Return the display device without actually sending the event indicating
         // that it was added.  The caller will handle it.
         return device;
@@ -98,23 +112,35 @@
         }
     }
 
-    private final class VirtualDisplayDevice extends DisplayDevice
-            implements DeathRecipient {
+    private void handleMediaProjectionStoppedLocked(IBinder appToken) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+        if (device != null) {
+            Slog.i(TAG, "Virtual display device released because media projection stopped: "
+                    + device.mName);
+            device.stopLocked();
+        }
+    }
+
+    private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
         private final IBinder mAppToken;
         private final int mOwnerUid;
         final String mOwnerPackageName;
-        private final String mName;
+        final String mName;
         private final int mWidth;
         private final int mHeight;
         private final int mDensityDpi;
         private final int mFlags;
+        private final Callbacks mCallbacks;
 
         private Surface mSurface;
         private DisplayDeviceInfo mInfo;
+        private int mState;
+        private boolean mStopped;
 
-        public VirtualDisplayDevice(IBinder displayToken,
-                IBinder appToken, int ownerUid, String ownerPackageName,
-                String name, int width, int height, int densityDpi, Surface surface, int flags) {
+        public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
+                int ownerUid, String ownerPackageName,
+                String name, int width, int height, int densityDpi, Surface surface, int flags,
+                Callbacks callbacks) {
             super(VirtualDisplayAdapter.this, displayToken);
             mAppToken = appToken;
             mOwnerUid = ownerUid;
@@ -125,6 +151,8 @@
             mDensityDpi = densityDpi;
             mSurface = surface;
             mFlags = flags;
+            mCallbacks = callbacks;
+            mState = Display.STATE_UNKNOWN;
         }
 
         @Override
@@ -142,6 +170,19 @@
                 mSurface = null;
             }
             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+            mCallbacks.dispatchDisplayStopped();
+        }
+
+        @Override
+        public void requestDisplayStateLocked(int state) {
+            if (state != mState) {
+                mState = state;
+                if (state == Display.STATE_OFF) {
+                    mCallbacks.dispatchDisplayPaused();
+                } else {
+                    mCallbacks.dispatchDisplayResumed();
+                }
+            }
         }
 
         @Override
@@ -150,7 +191,7 @@
         }
 
         public void setSurfaceLocked(Surface surface) {
-            if (mSurface != surface) {
+            if (!mStopped && mSurface != surface) {
                 if ((mSurface != null) != (surface != null)) {
                     sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
                 }
@@ -160,6 +201,20 @@
             }
         }
 
+        public void stopLocked() {
+            setSurfaceLocked(null);
+            mStopped = true;
+        }
+
+        @Override
+        public void dumpLocked(PrintWriter pw) {
+            super.dumpLocked(pw);
+            pw.println("mFlags=" + mFlags);
+            pw.println("mState=" + Display.stateToString(mState));
+            pw.println("mStopped=" + mStopped);
+        }
+
+
         @Override
         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
             if (mInfo == null) {
@@ -174,9 +229,11 @@
                 mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
                 mInfo.flags = 0;
                 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
-                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
-                            | DisplayDeviceInfo.FLAG_NEVER_BLANK
-                            | DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
+                    if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE) == 0) {
+                        mInfo.flags |=  DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY
+                                | DisplayDeviceInfo.FLAG_NEVER_BLANK;
+                    }
                 } else if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
                 }
@@ -195,4 +252,62 @@
             return mInfo;
         }
     }
+
+    private static class Callbacks extends Handler {
+        private static final int MSG_ON_DISPLAY_PAUSED = 0;
+        private static final int MSG_ON_DISPLAY_RESUMED = 1;
+        private static final int MSG_ON_DISPLAY_STOPPED = 2;
+
+        private final IVirtualDisplayCallbacks mCallbacks;
+
+        public Callbacks(IVirtualDisplayCallbacks callbacks, Handler handler) {
+            super(handler.getLooper());
+            mCallbacks = callbacks;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            try {
+                switch (msg.what) {
+                    case MSG_ON_DISPLAY_PAUSED:
+                        mCallbacks.onDisplayPaused();
+                        break;
+                    case MSG_ON_DISPLAY_RESUMED:
+                        mCallbacks.onDisplayResumed();
+                        break;
+                    case MSG_ON_DISPLAY_STOPPED:
+                        mCallbacks.onDisplayStopped();
+                        break;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify listener of virtual display event.", e);
+            }
+        }
+
+        public void dispatchDisplayPaused() {
+            sendEmptyMessage(MSG_ON_DISPLAY_PAUSED);
+        }
+
+        public void dispatchDisplayResumed() {
+            sendEmptyMessage(MSG_ON_DISPLAY_RESUMED);
+        }
+
+        public void dispatchDisplayStopped() {
+            sendEmptyMessage(MSG_ON_DISPLAY_STOPPED);
+        }
+    }
+
+    private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
+        private IBinder mAppToken;
+        public MediaProjectionCallback(IBinder appToken) {
+            mAppToken = appToken;
+        }
+
+        @Override
+        public void onStop() {
+            synchronized (getSyncRoot()) {
+                handleMediaProjectionStoppedLocked(mAppToken);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index 7e7f2e4..607805b 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -31,7 +31,8 @@
  * @hide
  */
 public class ActivityRecognitionProxy {
-    private final String TAG = "ActivityRecognitionProxy";
+    private static final String TAG = "ActivityRecognitionProxy";
+
     private final ServiceWatcher mServiceWatcher;
     private final ActivityRecognitionHardware mActivityRecognitionHardware;
 
@@ -85,6 +86,7 @@
 
         // try to bind the provider
         if (!activityRecognitionProxy.mServiceWatcher.start()) {
+            Log.e(TAG, "ServiceWatcher could not start.");
             return null;
         }
 
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
new file mode 100644
index 0000000..7b28699
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -0,0 +1,329 @@
+/*
+ * 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.media.projection;
+
+import com.android.server.Watchdog;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
+import android.media.projection.IMediaProjectionManager;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionCallback;
+import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manages MediaProjection sessions.
+ *
+ * The {@link MediaProjectionManagerService} manages the creation and lifetime of MediaProjections,
+ * as well as the capabilities they grant. Any service using MediaProjection tokens as permission
+ * grants <b>must</b> validate the token before use by calling {@link
+ * IMediaProjectionService#isValidMediaProjection}.
+ */
+public final class MediaProjectionManagerService extends SystemService
+        implements Watchdog.Monitor {
+    private static final String TAG = "MediaProjectionManagerService";
+
+    private final Object mLock = new Object(); // Protects the list of media projections
+    private final Map<IBinder, MediaProjection> mProjectionGrants;
+
+    private final Context mContext;
+    private final AppOpsManager mAppOps;
+
+    public MediaProjectionManagerService(Context context) {
+        super(context);
+        mContext = context;
+        mProjectionGrants = new ArrayMap<IBinder, MediaProjection>();
+        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        Watchdog.getInstance().addMonitor(this);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.MEDIA_PROJECTION_SERVICE, new BinderService(),
+                false /*allowIsolated*/);
+    }
+
+    @Override
+    public void monitor() {
+        synchronized (mLock) { /* check for deadlock */ }
+    }
+
+    private void dump(final PrintWriter pw) {
+        pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)");
+        synchronized (mLock) {
+            Collection<MediaProjection> projections = mProjectionGrants.values();
+            pw.println("Media Projections: size=" + projections.size());
+            for (MediaProjection mp : projections) {
+                mp.dump(pw, "  ");
+            }
+        }
+    }
+
+    private final class BinderService extends IMediaProjectionManager.Stub {
+
+        @Override // Binder call
+        public boolean hasProjectionPermission(int uid, String packageName) {
+            long token = Binder.clearCallingIdentity();
+            boolean hasPermission = false;
+            try {
+                hasPermission |= checkPermission(packageName,
+                        android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
+                        || mAppOps.checkOpNoThrow(
+                                AppOpsManager.OP_PROJECT_MEDIA, uid, packageName)
+                        == AppOpsManager.MODE_ALLOWED;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return hasPermission;
+        }
+
+        @Override // Binder call
+        public IMediaProjection createProjection(int uid, String packageName, int type,
+                boolean isPermanentGrant) {
+            if (mContext.checkCallingPermission(Manifest.permission.CREATE_MEDIA_PROJECTION)
+                        != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires CREATE_MEDIA_PROJECTION in order to grant "
+                        + "projection permission");
+            }
+            long callingToken = Binder.clearCallingIdentity();
+            MediaProjection projection;
+            try {
+                projection = new MediaProjection(type, uid, packageName);
+                if (isPermanentGrant) {
+                    mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA,
+                            projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(callingToken);
+            }
+            return projection;
+        }
+
+        @Override // Binder call
+        public boolean isValidMediaProjection(IMediaProjection projection) {
+            return mProjectionGrants.containsKey(projection.asBinder());
+        }
+
+        @Override // Binder call
+        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+            if (mContext == null
+                    || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump MediaProjectionManager from from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                dump(pw);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private boolean checkPermission(String packageName, String permission) {
+            return mContext.getPackageManager().checkPermission(permission, packageName)
+                    == PackageManager.PERMISSION_GRANTED;
+        }
+    }
+
+    private final class MediaProjection extends IMediaProjection.Stub implements DeathRecipient {
+        public int uid;
+        public String packageName;
+
+        private IBinder mToken;
+        private int mType;
+        private CallbackDelegate mCallbackDelegate;
+
+        public MediaProjection(int type, int uid, String packageName) {
+            mType = type;
+            this.uid = uid;
+            this.packageName = packageName;
+            mCallbackDelegate = new CallbackDelegate();
+        }
+
+        @Override // Binder call
+        public boolean canProjectVideo() {
+            return mType == MediaProjectionManager.TYPE_MIRRORING ||
+                    mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE;
+        }
+
+        @Override // Binder call
+        public boolean canProjectSecureVideo() {
+            return false;
+        }
+
+        @Override // Binder call
+        public boolean canProjectAudio() {
+            return mType == MediaProjectionManager.TYPE_MIRRORING ||
+                    mType == MediaProjectionManager.TYPE_PRESENTATION;
+        }
+
+        @Override // Binder call
+        public int getVirtualDisplayFlags() {
+            switch (mType) {
+                case MediaProjectionManager.TYPE_SCREEN_CAPTURE:
+                    return DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE;
+                case MediaProjectionManager.TYPE_MIRRORING:
+                    return DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+                            DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+                case MediaProjectionManager.TYPE_PRESENTATION:
+                    return DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION |
+                            DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+            }
+            throw new RuntimeException("Unknown MediaProjection type");
+        }
+
+        @Override // Binder call
+        public void start(IMediaProjectionCallback callback) {
+            if (callback == null) {
+                throw new IllegalArgumentException("callback must not be null");
+            }
+            synchronized (mLock) {
+                if (mProjectionGrants.containsKey(asBinder())) {
+                    throw new IllegalStateException(
+                            "Cannot start already started MediaProjection");
+                }
+                addCallback(callback);
+                try {
+                    mToken = callback.asBinder();
+                    mToken.linkToDeath(this, 0);
+                } catch (RemoteException e) {
+                    Slog.w(TAG,
+                            "MediaProjectionCallbacks must be valid, aborting MediaProjection", e);
+                    return;
+                }
+                mProjectionGrants.put(asBinder(), this);
+            }
+        }
+
+        @Override // Binder call
+        public void stop() {
+            synchronized (mLock) {
+                if (!mProjectionGrants.containsKey(asBinder())) {
+                    Slog.w(TAG, "Attempted to stop inactive MediaProjection "
+                            + "(uid=" + Binder.getCallingUid() + ", "
+                            + "pid=" + Binder.getCallingPid() + ")");
+                    return;
+                }
+                mToken.unlinkToDeath(this, 0);
+                mCallbackDelegate.dispatchStop();
+                mProjectionGrants.remove(asBinder());
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            stop();
+        }
+
+        @Override
+        public void addCallback(IMediaProjectionCallback callback) {
+            if (callback == null) {
+                throw new IllegalArgumentException("callback must not be null");
+            }
+            mCallbackDelegate.add(callback);
+        }
+
+        @Override
+        public void removeCallback(IMediaProjectionCallback callback) {
+            if (callback == null) {
+                throw new IllegalArgumentException("callback must not be null");
+            }
+            mCallbackDelegate.remove(callback);
+        }
+
+        public void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + "(" + packageName + ", uid=" + uid + "): " + typeToString(mType));
+        }
+    }
+
+    private static class CallbackDelegate {
+        private static final int MSG_ON_STOP = 0;
+        private List<IMediaProjectionCallback> mCallbacks;
+        private Handler mHandler;
+        private Object mLock = new Object();
+
+        public CallbackDelegate() {
+            mHandler = new Handler(Looper.getMainLooper(), null, true /*async*/);
+            mCallbacks = new ArrayList<IMediaProjectionCallback>();
+        }
+
+        public void add(IMediaProjectionCallback callback) {
+            synchronized (mLock) {
+                mCallbacks.add(callback);
+            }
+        }
+
+        public void remove(IMediaProjectionCallback callback) {
+            synchronized (mLock) {
+                mCallbacks.remove(callback);
+            }
+        }
+
+        public void dispatchStop() {
+            synchronized (mLock) {
+                for (final IMediaProjectionCallback callback : mCallbacks) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+                                callback.onStop();
+                            } catch (RemoteException e) {
+                                Slog.w(TAG, "Failed to notify media projection has stopped", e);
+                            }
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    private static String typeToString(int type) {
+        switch (type) {
+            case MediaProjectionManager.TYPE_SCREEN_CAPTURE:
+                return "TYPE_SCREEN_CAPTURE";
+            case MediaProjectionManager.TYPE_MIRRORING:
+                return "TYPE_MIRRORING";
+            case MediaProjectionManager.TYPE_PRESENTATION:
+                return "TYPE_PRESENTATION";
+        }
+        return Integer.toString(type);
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aa9e151..0e18776 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -220,6 +220,7 @@
     private static final int REASON_NOMAN_CANCEL_ALL = 9;
     private static final int REASON_LISTENER_CANCEL = 10;
     private static final int REASON_LISTENER_CANCEL_ALL = 11;
+    private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
 
     private static class Archive {
         final int mBufferSize;
@@ -2071,9 +2072,9 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
+                String listenerName = listener == null ? null : listener.component.toShortString();
                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
-                        mustHaveFlags, mustNotHaveFlags, reason,
-                        listener == null ? null : listener.component.toShortString());
+                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
 
                 synchronized (mNotificationList) {
                     int index = indexOfNotificationLocked(pkg, tag, id, userId);
@@ -2097,6 +2098,7 @@
                         mNotificationsByKey.remove(r.sbn.getKey());
 
                         cancelNotificationLocked(r, sendDelete, reason);
+                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
                         updateLightsLocked();
                     }
                 }
@@ -2135,13 +2137,14 @@
     boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
             int mustNotHaveFlags, boolean doit, int userId, int reason,
             ManagedServiceInfo listener) {
+        String listenerName = listener == null ? null : listener.component.toShortString();
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
                 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
-                listener == null ? null : listener.component.toShortString());
+                listenerName);
 
         synchronized (mNotificationList) {
             final int N = mNotificationList.size();
-            boolean canceledSomething = false;
+            ArrayList<NotificationRecord> canceledNotifications = null;
             for (int i = N-1; i >= 0; --i) {
                 NotificationRecord r = mNotificationList.get(i);
                 if (!notificationMatchesUserId(r, userId)) {
@@ -2160,7 +2163,10 @@
                 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
                     continue;
                 }
-                canceledSomething = true;
+                if (canceledNotifications == null) {
+                    canceledNotifications = new ArrayList<>();
+                }
+                canceledNotifications.add(r);
                 if (!doit) {
                     return true;
                 }
@@ -2168,19 +2174,27 @@
                 mNotificationsByKey.remove(r.sbn.getKey());
                 cancelNotificationLocked(r, false, reason);
             }
-            if (canceledSomething) {
+            if (doit && canceledNotifications != null) {
+                final int M = canceledNotifications.size();
+                for (int i = 0; i < M; i++) {
+                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
+                            listenerName);
+                }
+            }
+            if (canceledNotifications != null) {
                 updateLightsLocked();
             }
-            return canceledSomething;
+            return canceledNotifications != null;
         }
     }
 
     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
             ManagedServiceInfo listener, boolean includeCurrentProfiles) {
+        String listenerName = listener == null ? null : listener.component.toShortString();
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
-                null, userId, 0, 0, reason,
-                listener == null ? null : listener.component.toShortString());
+                null, userId, 0, 0, reason, listenerName);
 
+        ArrayList<NotificationRecord> canceledNotifications = null;
         final int N = mNotificationList.size();
         for (int i=N-1; i>=0; i--) {
             NotificationRecord r = mNotificationList.get(i);
@@ -2199,11 +2213,54 @@
                 mNotificationList.remove(i);
                 mNotificationsByKey.remove(r.sbn.getKey());
                 cancelNotificationLocked(r, true, reason);
+                // Make a note so we can cancel children later.
+                if (canceledNotifications == null) {
+                    canceledNotifications = new ArrayList<>();
+                }
+                canceledNotifications.add(r);
             }
         }
+        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
+        for (int i = 0; i < M; i++) {
+            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
+                    listenerName);
+        }
         updateLightsLocked();
     }
 
+    // Warning: The caller is responsible for invoking updateLightsLocked().
+    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
+            String listenerName) {
+        Notification n = r.getNotification();
+        if (n.getGroup() == null || (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0) {
+            return;
+        }
+
+        String pkg = r.sbn.getPackageName();
+        int userId = r.getUserId();
+
+        if (pkg == null) {
+            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
+            return;
+        }
+
+        final int N = mNotificationList.size();
+        for (int i = N - 1; i >= 0; i--) {
+            NotificationRecord childR = mNotificationList.get(i);
+            Notification childN = childR.getNotification();
+            StatusBarNotification childSbn = childR.sbn;
+            if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) &&
+                    n.getGroup().equals(childN.getGroup())) {
+                EventLogTags.writeNotificationCancel(callingUid, callingPid,
+                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
+                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
+                mNotificationList.remove(i);
+                mNotificationsByKey.remove(childR.getKey());
+                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
+            }
+        }
+    }
+
     // lock on mNotificationList
     void updateLightsLocked()
     {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 8f237db..252f2f4 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -20,8 +20,10 @@
 import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
 
 import android.content.Context;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiHotplugEvent;
+import android.hardware.hdmi.IHdmiDeviceEventListener;
 import android.media.AudioDevicePort;
 import android.media.AudioManager;
 import android.media.AudioPatch;
@@ -33,7 +35,6 @@
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvStreamConfig;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -66,27 +67,24 @@
     private final SparseArray<Connection> mConnections = new SparseArray<Connection>();
     private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
     private final Context mContext;
-    private final TvInputManagerService.Client mClient;
+    private final Listener mListener;
     private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
     private final AudioManager mAudioManager;
     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
     // TODO: Should handle INACTIVE case.
     private final SparseArray<TvInputInfo> mTvInputInfoMap = new SparseArray<TvInputInfo>();
+    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
 
-    // Calls to mClient should happen here.
-    private final HandlerThread mHandlerThread = new HandlerThread(TAG);
-    private final Handler mHandler;
+    // Calls to mListener should happen here.
+    private final Handler mHandler = new ListenerHandler();
 
     private final Object mLock = new Object();
 
-    public TvInputHardwareManager(Context context, TvInputManagerService.Client client) {
+    public TvInputHardwareManager(Context context, Listener listener) {
         mContext = context;
-        mClient = client;
+        mListener = listener;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mHal.init();
-
-        mHandlerThread.start();
-        mHandler = new ClientHandler(mHandlerThread.getLooper());
     }
 
     public void onBootPhase(int phase) {
@@ -105,7 +103,8 @@
             connection.updateConfigsLocked(configs);
             mConnections.put(info.getDeviceId(), connection);
             buildInfoListLocked();
-            // TODO: notify if necessary
+            mHandler.obtainMessage(
+                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
         }
     }
 
@@ -127,7 +126,8 @@
             connection.resetLocked(null, null, null, null, null);
             mConnections.remove(deviceId);
             buildInfoListLocked();
-            // TODO: notify if necessary
+            mHandler.obtainMessage(
+                    ListenerHandler.HARDWARE_DEVICE_REMOVED, deviceId, 0).sendToTarget();
         }
     }
 
@@ -191,7 +191,7 @@
             for (int i = 0; i < mHdmiStateMap.size(); ++i) {
                 String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i));
                 if (inputId != null && inputId.equals(info.getId())) {
-                    mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE,
+                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
                             convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
                             inputId).sendToTarget();
                 }
@@ -273,7 +273,7 @@
             if (inputId == null) {
                 return;
             }
-            mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE,
+            mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
                     convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
         }
     }
@@ -502,20 +502,48 @@
         }
     }
 
-    private class ClientHandler extends Handler {
-        private static final int DO_SET_AVAILABLE = 1;
+    interface Listener {
+        public void onStateChanged(String inputId, int state);
+        public void onHardwareDeviceAdded(TvInputHardwareInfo info);
+        public void onHardwareDeviceRemoved(int deviceId);
+        public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice);
+        public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice);
+    }
 
-        ClientHandler(Looper looper) {
-            super(looper);
-        }
+    private class ListenerHandler extends Handler {
+        private static final int STATE_CHANGED = 1;
+        private static final int HARDWARE_DEVICE_ADDED = 2;
+        private static final int HARDWARE_DEVICE_REMOVED = 3;
+        private static final int CEC_DEVICE_ADDED = 4;
+        private static final int CEC_DEVICE_REMOVED = 5;
 
         @Override
         public final void handleMessage(Message msg) {
             switch (msg.what) {
-                case DO_SET_AVAILABLE: {
+                case STATE_CHANGED: {
                     String inputId = (String) msg.obj;
                     int state = msg.arg1;
-                    mClient.setState(inputId, state);
+                    mListener.onStateChanged(inputId, state);
+                    break;
+                }
+                case HARDWARE_DEVICE_ADDED: {
+                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
+                    mListener.onHardwareDeviceAdded(info);
+                    break;
+                }
+                case HARDWARE_DEVICE_REMOVED: {
+                    int deviceId = msg.arg1;
+                    mListener.onHardwareDeviceRemoved(deviceId);
+                    break;
+                }
+                case CEC_DEVICE_ADDED: {
+                    HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
+                    mListener.onHdmiCecDeviceAdded(info);
+                    break;
+                }
+                case CEC_DEVICE_REMOVED: {
+                    HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
+                    mListener.onHdmiCecDeviceRemoved(info);
                     break;
                 }
                 default: {
@@ -525,4 +553,14 @@
             }
         }
     }
+
+    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
+        @Override
+        public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) {
+            mHandler.obtainMessage(
+                    activated ? ListenerHandler.CEC_DEVICE_ADDED
+                    : ListenerHandler.CEC_DEVICE_REMOVED,
+                    0, 0, deviceInfo).sendToTarget();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 20fdefa..69c693a 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -37,6 +37,7 @@
 import android.content.pm.ServiceInfo;
 import android.database.Cursor;
 import android.graphics.Rect;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.media.tv.ITvInputClient;
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
@@ -80,6 +81,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -113,7 +115,7 @@
         mContentResolver = context.getContentResolver();
         mLogHandler = new LogHandler(IoThread.get().getLooper());
 
-        mTvInputHardwareManager = new TvInputHardwareManager(context, new Client());
+        mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
 
         synchronized (mLock) {
             mUserStates.put(mCurrentUserId, new UserState());
@@ -129,6 +131,7 @@
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             registerBroadcastReceivers();
+        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mLock) {
                 buildTvInputListLocked(mCurrentUserId);
             }
@@ -201,12 +204,15 @@
         }, UserHandle.ALL, intentFilter, null, null);
     }
 
+    private static boolean hasHardwarePermission(PackageManager pm, ComponentName name) {
+        return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
+                name.getPackageName()) == PackageManager.PERMISSION_GRANTED;
+    }
+
     private void buildTvInputListLocked(int userId) {
         UserState userState = getUserStateLocked(userId);
 
-        Map<String, TvInputState> oldInputMap = userState.inputMap;
-        userState.inputMap = new HashMap<String, TvInputState>();
-
+        Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
         userState.packageSet.clear();
 
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
@@ -214,6 +220,7 @@
         List<ResolveInfo> services = pm.queryIntentServices(
                 new Intent(TvInputService.SERVICE_INTERFACE),
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        List<TvInputInfo> infoList = new ArrayList<TvInputInfo>();
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
             if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -222,23 +229,56 @@
                 continue;
             }
             try {
-                TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
-                if (DEBUG) Slog.d(TAG, "add " + info.getId());
-                TvInputState state = oldInputMap.get(info.getId());
-                if (state == null) {
-                    state = new TvInputState();
+                infoList.clear();
+                ComponentName service = new ComponentName(si.packageName, si.name);
+                if (hasHardwarePermission(pm, service)) {
+                    ServiceState serviceState = userState.serviceStateMap.get(service);
+                    if (serviceState == null) {
+                        // We see this hardware TV input service for the first time; we need to
+                        // prepare the ServiceState object so that we can connect to the service and
+                        // let it add TvInputInfo objects to mInputList if there's any.
+                        serviceState = new ServiceState(service, userId);
+                        userState.serviceStateMap.put(service, serviceState);
+                    } else {
+                        infoList.addAll(serviceState.mInputList);
+                    }
+                } else {
+                    infoList.add(TvInputInfo.createTvInputInfo(mContext, ri));
                 }
-                userState.inputMap.put(info.getId(), state);
-                state.mInfo = info;
-                userState.packageSet.add(si.packageName);
+
+                for (TvInputInfo info : infoList) {
+                    if (DEBUG) Slog.d(TAG, "add " + info.getId());
+                    TvInputState state = userState.inputMap.get(info.getId());
+                    if (state == null) {
+                        state = new TvInputState();
+                    }
+                    state.mInfo = info;
+                    inputMap.put(info.getId(), state);
+                }
 
                 // Reconnect the service if existing input is updated.
-                updateServiceConnectionLocked(info.getId(), userId);
+                updateServiceConnectionLocked(service, userId);
+
+                userState.packageSet.add(si.packageName);
             } catch (IOException | XmlPullParserException e) {
                 Slog.e(TAG, "Can't load TV input " + si.name, e);
             }
         }
-        oldInputMap.clear();
+
+        for (String inputId : inputMap.keySet()) {
+            if (!userState.inputMap.containsKey(inputId)) {
+                notifyInputAddedLocked(userState, inputId);
+            }
+        }
+
+        for (String inputId : userState.inputMap.keySet()) {
+            if (!inputMap.containsKey(inputId)) {
+                notifyInputRemovedLocked(userState, inputId);
+            }
+        }
+
+        userState.inputMap.clear();
+        userState.inputMap = inputMap;
     }
 
     private void switchUser(int userId) {
@@ -305,11 +345,11 @@
         return userState;
     }
 
-    private ServiceState getServiceStateLocked(String inputId, int userId) {
+    private ServiceState getServiceStateLocked(ComponentName name, int userId) {
         UserState userState = getUserStateLocked(userId);
-        ServiceState serviceState = userState.serviceStateMap.get(inputId);
+        ServiceState serviceState = userState.serviceStateMap.get(name);
         if (serviceState == null) {
-            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
+            throw new IllegalStateException("Service state not found for " + name + " (userId="
                     + userId + ")");
         }
         return serviceState;
@@ -344,9 +384,16 @@
                 false, methodName, null);
     }
 
-    private void updateServiceConnectionLocked(String inputId, int userId) {
+    private static boolean shouldMaintainConnection(ServiceState serviceState) {
+        return !serviceState.mClientTokens.isEmpty()
+                || !serviceState.mSessionTokens.isEmpty()
+                || serviceState.mIsHardware;
+        // TODO: Find a way to maintain connection only when necessary.
+    }
+
+    private void updateServiceConnectionLocked(ComponentName service, int userId) {
         UserState userState = getUserStateLocked(userId);
-        ServiceState serviceState = userState.serviceStateMap.get(inputId);
+        ServiceState serviceState = userState.serviceStateMap.get(service);
         if (serviceState == null) {
             return;
         }
@@ -357,9 +404,8 @@
             }
             serviceState.mReconnecting = false;
         }
-        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
-                && serviceState.mSessionTokens.isEmpty();
-        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
+        boolean maintainConnection = shouldMaintainConnection(serviceState);
+        if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) {
             // This means that the service is not yet connected but its state indicates that we
             // have pending requests. Then, connect the service.
             if (serviceState.mBound) {
@@ -368,25 +414,23 @@
                 return;
             }
             if (DEBUG) {
-                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
-                        + ")");
+                Slog.d(TAG, "bindServiceAsUser(service=" + service + ", userId=" + userId + ")");
             }
 
-            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
-                    userState.inputMap.get(inputId).mInfo.getComponent());
+            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(service);
             // Binding service may fail if the service is updating.
             // In that case, the connection will be revived in buildTvInputListLocked called by
             // onSomePackagesChanged.
             serviceState.mBound = mContext.bindServiceAsUser(
                     i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
-        } else if (serviceState.mService != null && isStateEmpty) {
+        } else if (serviceState.mService != null && !maintainConnection) {
             // This means that the service is already connected but its state indicates that we have
             // nothing to do with it. Then, disconnect the service.
             if (DEBUG) {
-                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
+                Slog.d(TAG, "unbindService(service=" + service + ")");
             }
             mContext.unbindService(serviceState.mConnection);
-            userState.serviceStateMap.remove(inputId);
+            userState.serviceStateMap.remove(service);
         }
     }
 
@@ -407,7 +451,7 @@
         final UserState userState = getUserStateLocked(userId);
         final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
+            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")");
         }
 
         final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
@@ -417,14 +461,14 @@
             @Override
             public void onSessionCreated(ITvInputSession session) {
                 if (DEBUG) {
-                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
+                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
                 }
                 synchronized (mLock) {
                     sessionState.mSession = session;
                     if (session == null) {
                         removeSessionStateLocked(sessionToken, userId);
-                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
-                                null, null, sessionState.mSeq);
+                        sendSessionTokenToClientLocked(sessionState.mClient,
+                                sessionState.mInfo.getId(), null, null, sessionState.mSeq);
                     } else {
                         try {
                             session.asBinder().linkToDeath(sessionState, 0);
@@ -439,8 +483,9 @@
                         }
                         clientState.mSessionTokens.add(sessionState.mSessionToken);
 
-                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
-                                sessionToken, channels[0], sessionState.mSeq);
+                        sendSessionTokenToClientLocked(sessionState.mClient,
+                                sessionState.mInfo.getId(), sessionToken, channels[0],
+                                sessionState.mSeq);
                     }
                     channels[0].dispose();
                 }
@@ -520,6 +565,23 @@
             }
 
             @Override
+            public void onContentBlocked(String rating) {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "onContentBlocked()");
+                    }
+                    if (sessionState.mSession == null || sessionState.mClient == null) {
+                        return;
+                    }
+                    try {
+                        sessionState.mClient.onContentBlocked(rating, sessionState.mSeq);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in onContentBlocked");
+                    }
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs) {
                 synchronized (mLock) {
                     if (DEBUG) {
@@ -540,12 +602,12 @@
 
         // Create a session. When failed, send a null token immediately.
         try {
-            service.createSession(channels[1], callback);
+            service.createSession(channels[1], callback, sessionState.mInfo.getId());
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
-            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
-                    sessionState.mSeq);
+            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null,
+                    null, sessionState.mSeq);
         }
         channels[1].dispose();
     }
@@ -595,11 +657,14 @@
             }
         }
 
-        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
-        if (serviceState != null) {
-            serviceState.mSessionTokens.remove(sessionToken);
+        TvInputInfo info = sessionState.mInfo;
+        if (info != null) {
+            ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
+            if (serviceState != null) {
+                serviceState.mSessionTokens.remove(sessionToken);
+            }
         }
-        updateServiceConnectionLocked(sessionState.mInputId, userId);
+        updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
     }
 
     private void unregisterClientInternalLocked(IBinder clientToken, String inputId,
@@ -613,7 +678,11 @@
             }
         }
 
-        ServiceState serviceState = userState.serviceStateMap.get(inputId);
+        TvInputInfo info = userState.inputMap.get(inputId).mInfo;
+        if (info == null) {
+            return;
+        }
+        ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
         if (serviceState == null) {
             return;
         }
@@ -633,14 +702,40 @@
             Slog.e(TAG, "error in unregisterCallback", e);
         } finally {
             serviceState.mCallback = null;
-            updateServiceConnectionLocked(inputId, userId);
+            updateServiceConnectionLocked(info.getComponent(), userId);
         }
     }
 
-    private void notifyStateChangedLocked(UserState userState, String inputId,
+    private void notifyInputAddedLocked(UserState userState, String inputId) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyInputAdded: inputId = " + inputId);
+        }
+        for (ITvInputManagerCallback callback : userState.callbackSet) {
+            try {
+                callback.onInputAdded(inputId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to report added input to callback.");
+            }
+        }
+    }
+
+    private void notifyInputRemovedLocked(UserState userState, String inputId) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyInputRemovedLocked: inputId = " + inputId);
+        }
+        for (ITvInputManagerCallback callback : userState.callbackSet) {
+            try {
+                callback.onInputRemoved(inputId);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to report removed input to callback.");
+            }
+        }
+    }
+
+    private void notifyInputStateChangedLocked(UserState userState, String inputId,
             int state, ITvInputManagerCallback targetCallback) {
         if (DEBUG) {
-            Slog.d(TAG, "notifyStateChangedLocked: inputId = " + inputId
+            Slog.d(TAG, "notifyInputStateChangedLocked: inputId = " + inputId
                     + "; state = " + state);
         }
         if (targetCallback == null) {
@@ -666,14 +761,13 @@
         ServiceState serviceState = userState.serviceStateMap.get(inputId);
         int oldState = inputState.mState;
         inputState.mState = state;
-        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
-                && serviceState.mSessionTokens.isEmpty();
-        if (serviceState != null && serviceState.mService == null && !isStateEmpty) {
+        if (serviceState != null && serviceState.mService == null
+                && shouldMaintainConnection(serviceState)) {
             // We don't notify state change while reconnecting. It should remain disconnected.
             return;
         }
         if (oldState != state) {
-            notifyStateChangedLocked(userState, inputId, state, null);
+            notifyInputStateChangedLocked(userState, inputId, state, null);
         }
     }
 
@@ -698,6 +792,22 @@
         }
 
         @Override
+        public TvInputInfo getTvInputInfo(String inputId, int userId) {
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "getTvInputInfo");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getUserStateLocked(resolvedUserId);
+                    TvInputState state = userState.inputMap.get(inputId);
+                    return state == null ? null : state.mInfo;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void registerCallback(final ITvInputManagerCallback callback, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "registerCallback");
@@ -707,7 +817,7 @@
                     UserState userState = getUserStateLocked(resolvedUserId);
                     userState.callbackSet.add(callback);
                     for (TvInputState state : userState.inputMap.values()) {
-                        notifyStateChangedLocked(userState, state.mInfo.getId(),
+                        notifyInputStateChangedLocked(userState, state.mInfo.getId(),
                                 state.mState, callback);
                     }
                 }
@@ -741,11 +851,11 @@
             try {
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
+                    TvInputInfo info = userState.inputMap.get(inputId).mInfo;
+                    ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                     if (serviceState == null) {
-                        serviceState = new ServiceState(
-                                userState.inputMap.get(inputId).mInfo, resolvedUserId);
-                        userState.serviceStateMap.put(inputId, serviceState);
+                        serviceState = new ServiceState(info.getComponent(), resolvedUserId);
+                        userState.serviceStateMap.put(info.getComponent(), serviceState);
                     }
                     // Send a null token immediately while reconnecting.
                     if (serviceState.mReconnecting == true) {
@@ -755,7 +865,7 @@
 
                     // Create a new session token and a session state.
                     IBinder sessionToken = new Binder();
-                    SessionState sessionState = new SessionState(sessionToken, inputId, client,
+                    SessionState sessionState = new SessionState(sessionToken, info, client,
                             seq, callingUid, resolvedUserId);
 
                     // Add them to the global session state map of the current user.
@@ -768,7 +878,7 @@
                         createSessionInternalLocked(serviceState.mService, sessionToken,
                                 resolvedUserId);
                     } else {
-                        updateServiceConnectionLocked(inputId, resolvedUserId);
+                        updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
                     }
                 }
             } finally {
@@ -816,6 +926,27 @@
         }
 
         @Override
+        public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
+                int height, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "dispatchSurfaceChanged");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .dispatchSurfaceChanged(format, width, height);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in dispatchSurfaceChanged", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void setVolume(IBinder sessionToken, float volume, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -861,8 +992,7 @@
                         }
 
                         // Create a log entry and fill it later.
-                        String packageName = userState.inputMap.get(sessionState.mInputId).mInfo
-                                .getServiceInfo().packageName;
+                        String packageName = sessionState.mInfo.getServiceInfo().packageName;
                         ContentValues values = new ContentValues();
                         values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
                         values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
@@ -888,6 +1018,26 @@
         }
 
         @Override
+        public void unblockContent(IBinder sessionToken, String unblockedRating, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "unblockContent");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .unblockContent(unblockedRating);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in unblockContent", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void setCaptionEnabled(IBinder sessionToken, boolean enabled, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -1025,6 +1175,7 @@
 
         @Override
         public void registerTvInputInfo(TvInputInfo info, int deviceId) {
+            // TODO: Revisit to sort out deviceId ownership.
             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
                     != PackageManager.PERMISSION_GRANTED) {
                 return;
@@ -1107,8 +1258,8 @@
 
                     pw.println("inputMap: inputId -> TvInputState");
                     pw.increaseIndent();
-                    for (TvInputState state : userState.inputMap.values()) {
-                        pw.println(state.toString());
+                    for (Map.Entry<String, TvInputState> entry: userState.inputMap.entrySet()) {
+                        pw.println(entry.getKey() + ": " + entry.getValue());
                     }
                     pw.decreaseIndent();
 
@@ -1149,9 +1300,9 @@
                     }
                     pw.decreaseIndent();
 
-                    pw.println("serviceStateMap: inputId -> ServiceState");
+                    pw.println("serviceStateMap: ComponentName -> ServiceState");
                     pw.increaseIndent();
-                    for (Map.Entry<String, ServiceState> entry :
+                    for (Map.Entry<ComponentName, ServiceState> entry :
                             userState.serviceStateMap.entrySet()) {
                         ServiceState service = entry.getValue();
                         pw.println(entry.getKey() + ": " + service);
@@ -1189,7 +1340,7 @@
                         pw.println(entry.getKey() + ": " + session);
 
                         pw.increaseIndent();
-                        pw.println("mInputId: " + session.mInputId);
+                        pw.println("mInfo: " + session.mInfo);
                         pw.println("mClient: " + session.mClient);
                         pw.println("mSeq: " + session.mSeq);
                         pw.println("mCallingUid: " + session.mCallingUid);
@@ -1239,8 +1390,8 @@
                 new HashMap<IBinder, ClientState>();
 
         // A mapping from the name of a TV input service to its state.
-        private final Map<String, ServiceState> serviceStateMap =
-                new HashMap<String, ServiceState>();
+        private final Map<ComponentName, ServiceState> serviceStateMap =
+                new HashMap<ComponentName, ServiceState>();
 
         // A mapping from the token of a TV input session to its state.
         private final Map<IBinder, SessionState> sessionStateMap =
@@ -1293,21 +1444,24 @@
         private final List<IBinder> mClientTokens = new ArrayList<IBinder>();
         private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
         private final ServiceConnection mConnection;
-        private final TvInputInfo mTvInputInfo;
+        private final ComponentName mName;
+        private final boolean mIsHardware;
+        private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>();
 
         private ITvInputService mService;
         private ServiceCallback mCallback;
         private boolean mBound;
         private boolean mReconnecting;
 
-        private ServiceState(TvInputInfo inputInfo, int userId) {
-            mTvInputInfo = inputInfo;
-            mConnection = new InputServiceConnection(inputInfo, userId);
+        private ServiceState(ComponentName name, int userId) {
+            mName = name;
+            mConnection = new InputServiceConnection(name, userId);
+            mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mName);
         }
     }
 
     private final class SessionState implements IBinder.DeathRecipient {
-        private final String mInputId;
+        private final TvInputInfo mInfo;
         private final ITvInputClient mClient;
         private final int mSeq;
         private final int mCallingUid;
@@ -1316,10 +1470,10 @@
         private ITvInputSession mSession;
         private Uri mLogUri;
 
-        private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq,
+        private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client, int seq,
                 int callingUid, int userId) {
             mSessionToken = sessionToken;
-            mInputId = inputId;
+            mInfo = info;
             mClient = client;
             mSeq = seq;
             mCallingUid = callingUid;
@@ -1343,28 +1497,29 @@
     }
 
     private final class InputServiceConnection implements ServiceConnection {
-        private final TvInputInfo mTvInputInfo;
+        private final ComponentName mName;
         private final int mUserId;
 
-        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
+        private InputServiceConnection(ComponentName name, int userId) {
+            mName = name;
             mUserId = userId;
-            mTvInputInfo = inputInfo;
         }
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            String inputId = mTvInputInfo.getId();
             if (DEBUG) {
-                Slog.d(TAG, "onServiceConnected(inputId=" + inputId + ")");
+                Slog.d(TAG, "onServiceConnected(name=" + name + ")");
             }
             synchronized (mLock) {
+                List<TvInputHardwareInfo> hardwareInfoList =
+                        mTvInputHardwareManager.getHardwareList();
                 UserState userState = getUserStateLocked(mUserId);
-                ServiceState serviceState = userState.serviceStateMap.get(inputId);
+                ServiceState serviceState = userState.serviceStateMap.get(mName);
                 serviceState.mService = ITvInputService.Stub.asInterface(service);
 
                 // Register a callback, if we need to.
-                if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
-                    serviceState.mCallback = new ServiceCallback(mTvInputInfo.getId(), mUserId);
+                if (serviceState.mIsHardware && serviceState.mCallback == null) {
+                    serviceState.mCallback = new ServiceCallback(mName, mUserId);
                     try {
                         serviceState.mService.registerCallback(serviceState.mCallback);
                     } catch (RemoteException e) {
@@ -1377,10 +1532,24 @@
                     createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
                 }
 
-                TvInputState inputState = userState.inputMap.get(inputId);
-                if (inputState != null && inputState.mState != INPUT_STATE_DISCONNECTED) {
-                    notifyStateChangedLocked(userState, mTvInputInfo.getId(),
-                            inputState.mState, null);
+                for (TvInputState inputState : userState.inputMap.values()) {
+                    if (inputState.mInfo.getComponent().equals(name)
+                            && inputState.mState != INPUT_STATE_DISCONNECTED) {
+                        notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+                                inputState.mState, null);
+                    }
+                }
+
+                if (serviceState.mIsHardware) {
+                    for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
+                        try {
+                            serviceState.mService.notifyHardwareAdded(hardwareInfo);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "error in notifyHardwareAdded", e);
+                        }
+                    }
+
+                    // TODO: Grab CEC devices and notify them to the service.
                 }
             }
         }
@@ -1388,15 +1557,15 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DEBUG) {
-                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
+                Slog.d(TAG, "onServiceDisconnected(name=" + name + ")");
             }
-            if (!mTvInputInfo.getComponent().equals(name)) {
+            if (!mName.equals(name)) {
                 throw new IllegalArgumentException("Mismatched ComponentName: "
-                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
+                        + mName + " (expected), " + name + " (actual).");
             }
             synchronized (mLock) {
                 UserState userState = getUserStateLocked(mUserId);
-                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
+                ServiceState serviceState = userState.serviceStateMap.get(mName);
                 if (serviceState != null) {
                     serviceState.mReconnecting = true;
                     serviceState.mBound = false;
@@ -1409,35 +1578,72 @@
                         if (sessionState.mSession == null) {
                             removeSessionStateLocked(sessionToken, sessionState.mUserId);
                             sendSessionTokenToClientLocked(sessionState.mClient,
-                                    sessionState.mInputId, null, null, sessionState.mSeq);
+                                    sessionState.mInfo.getId(), null, null, sessionState.mSeq);
                         }
                     }
 
-                    notifyStateChangedLocked(userState, mTvInputInfo.getId(),
-                            INPUT_STATE_DISCONNECTED, null);
-                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
+                    for (TvInputState inputState : userState.inputMap.values()) {
+                        if (inputState.mInfo.getComponent().equals(name)) {
+                            notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+                                    INPUT_STATE_DISCONNECTED, null);
+                        }
+                    }
+                    updateServiceConnectionLocked(mName, mUserId);
                 }
             }
         }
     }
 
     private final class ServiceCallback extends ITvInputServiceCallback.Stub {
-        private final String mInputId;
+        private final ComponentName mName;
         private final int mUserId;
 
-        ServiceCallback(String inputId, int userId) {
-            mInputId = inputId;
+        ServiceCallback(ComponentName name, int userId) {
+            mName = name;
             mUserId = userId;
         }
 
         @Override
-        public void onInputStateChanged(int state) {
-            if (DEBUG) {
-                Slog.d(TAG, "onInputStateChanged(inputId=" + mInputId + ", state="
-                        + state + ")");
-            }
+        public void addTvInput(TvInputInfo inputInfo) {
             synchronized (mLock) {
-                setStateLocked(mInputId, state, mUserId);
+                if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    Slog.w(TAG, "The caller does not have permission to add a TV input ("
+                            + inputInfo + ").");
+                    return;
+                }
+
+                ServiceState serviceState = getServiceStateLocked(mName, mUserId);
+                serviceState.mInputList.add(inputInfo);
+                buildTvInputListLocked(mUserId);
+            }
+        }
+
+        @Override
+        public void removeTvInput(String inputId) {
+            synchronized (mLock) {
+                if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    Slog.w(TAG, "The caller does not have permission to remove a TV input ("
+                            + inputId + ").");
+                    return;
+                }
+
+                ServiceState serviceState = getServiceStateLocked(mName, mUserId);
+                boolean removed = false;
+                for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
+                        it.hasNext(); ) {
+                    if (it.next().getId().equals(inputId)) {
+                        it.remove();
+                        removed = true;
+                        break;
+                    }
+                }
+                if (removed) {
+                    buildTvInputListLocked(mUserId);
+                } else {
+                    Slog.e(TAG, "TvInputInfo with inputId=" + inputId + " not found.");
+                }
             }
         }
     }
@@ -1586,11 +1792,58 @@
         }
     }
 
-    final class Client {
-        public void setState(String inputId, int state) {
+    final class HardwareListener implements TvInputHardwareManager.Listener {
+        @Override
+        public void onStateChanged(String inputId, int state) {
             synchronized (mLock) {
                 setStateLocked(inputId, state, mCurrentUserId);
             }
         }
+
+        @Override
+        public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
+            synchronized (mLock) {
+                UserState userState = getUserStateLocked(mCurrentUserId);
+                // Broadcast the event to all hardware inputs.
+                for (ServiceState serviceState : userState.serviceStateMap.values()) {
+                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    try {
+                        serviceState.mService.notifyHardwareAdded(info);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in notifyHardwareAdded", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onHardwareDeviceRemoved(int deviceId) {
+            synchronized (mLock) {
+                UserState userState = getUserStateLocked(mCurrentUserId);
+                // Broadcast the event to all hardware inputs.
+                for (ServiceState serviceState : userState.serviceStateMap.values()) {
+                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    try {
+                        serviceState.mService.notifyHardwareRemoved(deviceId);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in notifyHardwareRemoved", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice) {
+            synchronized (mLock) {
+                // TODO
+            }
+        }
+
+        @Override
+        public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice) {
+            synchronized (mLock) {
+                // TODO
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index e8ae97c..60724e7 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -16,10 +16,15 @@
 
 package com.android.server.webkit;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Binder;
 import android.os.Process;
 import android.util.Log;
 import android.webkit.IWebViewUpdateService;
+import android.webkit.WebViewFactory;
 
 /**
  * Private service to wait for the updatable WebView to be ready for use.
@@ -32,7 +37,22 @@
     private boolean mRelroReady32Bit = false;
     private boolean mRelroReady64Bit = false;
 
-    public WebViewUpdateService() {
+    private BroadcastReceiver mWebViewUpdatedReceiver;
+
+    public WebViewUpdateService(Context context) {
+        mWebViewUpdatedReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    String webviewPackage = "package:" + WebViewFactory.getWebViewPackageName();
+                    if (webviewPackage.equals(intent.getDataString())) {
+                        onWebViewUpdateInstalled();
+                    }
+                }
+        };
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addDataScheme("package");
+        context.registerReceiver(mWebViewUpdatedReceiver, filter);
     }
 
     /**
@@ -75,4 +95,14 @@
             }
         }
     }
+
+    private void onWebViewUpdateInstalled() {
+        Log.d(TAG, "WebView Package updated!");
+
+        synchronized (this) {
+            mRelroReady32Bit = false;
+            mRelroReady64Bit = false;
+        }
+        WebViewFactory.prepareWebViewInSystemServer();
+    }
 }
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
index 673a6e2..e842eeb 100644
--- a/services/core/jni/com_android_server_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
@@ -21,9 +21,13 @@
 #include <utils/misc.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
+#include <utils/Log.h>
+
 
 #include <inttypes.h>
 #include <fcntl.h>
+#include <errno.h>
+#include <string.h>
 
 namespace android {
 
@@ -40,7 +44,39 @@
         return size;
     }
 
-    static jlong com_android_server_PeristentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath) {
+    int wipe_block_device(int fd)
+    {
+        uint64_t range[2];
+        int ret;
+        uint64_t len = get_block_device_size(fd);
+
+        range[0] = 0;
+        range[1] = len;
+
+        if (range[1] == 0)
+            return 0;
+
+        ret = ioctl(fd, BLKSECDISCARD, &range);
+        if (ret < 0) {
+            ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
+            range[0] = 0;
+            range[1] = len;
+            ret = ioctl(fd, BLKDISCARD, &range);
+            if (ret < 0) {
+                ALOGE("Discard failed: %s\n", strerror(errno));
+                return -1;
+            } else {
+                ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
+                return 0;
+            }
+
+        }
+
+        return ret;
+    }
+
+    static jlong com_android_server_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
+    {
         const char *path = env->GetStringUTFChars(jpath, 0);
         int fd = open(path, O_RDONLY);
 
@@ -50,9 +86,20 @@
         return get_block_device_size(fd);
     }
 
+    static int com_android_server_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
+        const char *path = env->GetStringUTFChars(jpath, 0);
+        int fd = open(path, O_WRONLY);
+
+        if (fd < 0)
+            return 0;
+
+        return wipe_block_device(fd);
+    }
+
     static JNINativeMethod sMethods[] = {
          /* name, signature, funcPtr */
-        {"getBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PeristentDataBlockService_getBlockDeviceSize},
+        {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PersistentDataBlockService_getBlockDeviceSize},
+        {"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_PersistentDataBlockService_wipe},
     };
 
     int register_android_server_PersistentDataBlockService(JNIEnv* env)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6030cf..de929cb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -72,6 +72,7 @@
 import com.android.server.lights.LightsService;
 import com.android.server.media.MediaRouterService;
 import com.android.server.media.MediaSessionService;
+import com.android.server.media.projection.MediaProjectionManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
 import com.android.server.notification.NotificationManagerService;
@@ -411,7 +412,7 @@
             SystemConfig.getInstance();
 
             Slog.i(TAG, "WebView Update Service");
-            ServiceManager.addService("webviewupdate", new WebViewUpdateService());
+            ServiceManager.addService("webviewupdate", new WebViewUpdateService(context));
 
             Slog.i(TAG, "WebViewFactory preparation");
             WebViewFactory.prepareWebViewInSystemServer();
@@ -949,6 +950,10 @@
             mSystemServiceManager.startService(LauncherAppsService.class);
         }
 
+        if (!disableNonCoreServices) {
+            mSystemServiceManager.startService(MediaProjectionManagerService.class);
+        }
+
         // Before things start rolling, be sure we have decided whether
         // we are in safe mode.
         final boolean safeMode = wm.detectSafeMode();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index bed85fc..27bec9f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -105,17 +105,18 @@
         if (db.insertWithOnConflict(
                 SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
             for (Keyphrase keyphrase : soundModel.keyphrases) {
-                status &= addOrUpdateKeyphrase(soundModel.uuid, keyphrase);
+                status &= addOrUpdateKeyphrase(db, soundModel.uuid, keyphrase);
             }
+            db.close();
             return status;
         } else {
             Slog.w(TAG, "Failed to persist sound model to database");
+            db.close();
             return false;
         }
     }
 
-    private boolean addOrUpdateKeyphrase(UUID modelId, Keyphrase keyphrase) {
-        SQLiteDatabase db = getWritableDatabase();
+    private boolean addOrUpdateKeyphrase(SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
         ContentValues values = new ContentValues();
         values.put(KeyphraseContract.KEY_ID, keyphrase.id);
         values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
@@ -148,6 +149,7 @@
             Slog.w(TAG, "No keyphrases deleted from the database");
             status = false;
         }
+        db.close();
         return status;
     }
 
@@ -157,7 +159,7 @@
     public List<KeyphraseSoundModel> getKephraseSoundModels() {
         List<KeyphraseSoundModel> models = new ArrayList<>();
         String selectQuery = "SELECT  * FROM " + SoundModelContract.TABLE;
-        SQLiteDatabase db = this.getReadableDatabase();
+        SQLiteDatabase db = getReadableDatabase();
         Cursor c = db.rawQuery(selectQuery, null);
 
         // looping through all rows and adding to list
@@ -172,17 +174,18 @@
                 byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
                 // Get all the keyphrases for this this sound model.
                 models.add(new KeyphraseSoundModel(
-                        UUID.fromString(id), data, getKeyphrasesForSoundModel(id)));
+                        UUID.fromString(id), data, getKeyphrasesForSoundModel(db, id)));
             } while (c.moveToNext());
         }
+        c.close();
+        db.close();
         return models;
     }
 
-    private Keyphrase[] getKeyphrasesForSoundModel(String modelId) {
+    private Keyphrase[] getKeyphrasesForSoundModel(SQLiteDatabase db, String modelId) {
         List<Keyphrase> keyphrases = new ArrayList<>();
         String selectQuery = "SELECT  * FROM " + KeyphraseContract.TABLE
                 + " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
-        SQLiteDatabase db = this.getReadableDatabase();
         Cursor c = db.rawQuery(selectQuery, null);
 
         // looping through all rows and adding to list
@@ -199,6 +202,7 @@
         }
         Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
         keyphrases.toArray(keyphraseArr);
+        c.close();
         return keyphraseArr;
     }
 }
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 130364f..c050f30 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -37,6 +37,7 @@
         public void onHandleChanged(Connection c, Uri newHandle, int presentation) {}
         public void onCallerDisplayNameChanged(
                 Connection c, String callerDisplayName, int presentation) {}
+        public void onVideoStateChanged(Connection c, int videoState) {}
         public void onSignalChanged(Connection c, Bundle details) {}
         public void onDisconnected(Connection c, int cause, String message) {}
         public void onPostDialWait(Connection c, String remaining) {}
@@ -75,6 +76,7 @@
     private CallVideoProvider mCallVideoProvider;
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
+    private int mVideoState;
 
     /**
      * Create a new Connection.
@@ -118,6 +120,19 @@
     }
 
     /**
+     * Returns the video state of the call.
+     * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
+     *
+     * @return The video state of the call.
+     */
+    public final int getVideoState() {
+        return mVideoState;
+    }
+
+    /**
      * @return The audio state of the call, describing how its audio is currently
      *         being routed by the system. This is {@code null} if this Connection
      *         does not directly know about its audio state.
@@ -285,6 +300,23 @@
     }
 
     /**
+     * Set the video state for the connection.
+     * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
+     *
+     * @param videoState The new video state.
+     */
+    public final void setVideoState(int videoState) {
+        Log.d(this, "setVideoState %d", videoState);
+        mVideoState = videoState;
+        for (Listener l : mListeners) {
+            l.onVideoStateChanged(this, mVideoState);
+        }
+    }
+
+    /**
      * Sets state to active (e.g., an ongoing call where two or more parties can actively
      * communicate).
      */
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 5855470..9e04c6e 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -324,6 +324,13 @@
         }
 
         @Override
+        public void onVideoStateChanged(Connection c, int videoState) {
+            String id = mIdByConnection.get(c);
+            Log.d(this, "Adapter set video state %d", videoState);
+            mAdapter.setVideoState(id, videoState);
+        }
+
+        @Override
         public void onHandleChanged(Connection c, Uri handle, int presentation) {
             String id = mIdByConnection.get(c);
             mAdapter.setHandle(id, handle, presentation);
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index a812fa4..63546d3 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -323,4 +323,25 @@
             }
         }
     }
+
+    /**
+     * Sets the video state associated with a call.
+     *
+     * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
+     * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
+     *
+     * @param callId The unique ID of the call to set the video state for.
+     * @param videoState The video state.
+     */
+    void setVideoState(String callId, int videoState) {
+        Log.v(this, "setVideoState: %d", videoState);
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.setVideoState(callId, videoState);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index b246d92..bb335c3 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -16,8 +16,10 @@
 
 package android.telecomm;
 
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import android.content.ComponentName;
-import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,55 +28,27 @@
 /**
  * Represents a distinct account, line of service or call placement method that
  * the system can use to place phone calls.
+ *
+ * TODO: Per feedback from API Council, rename to "PhoneAccountHandle". See also comment on class
+ * PhoneAccountMetadata.
  */
 public class PhoneAccount implements Parcelable {
 
-
     /**
-     * Flag indicating that this {@code PhoneAccount} can act as a call manager for traditional
-     * SIM-based telephony calls. The {@link ConnectionService} associated with this phone-account
-     * will be allowed to manage SIM-based phone calls including using its own proprietary
-     * phone-call implementation (like VoIP calling) to make calls instead of the telephony stack.
-     * When a user opts to place a call using the SIM-based telephony stack, the connection-service
-     * associated with this phone-account will be attempted first if the user has explicitly
-     * selected it to be used as the default call-manager.
+     * Flag indicating that this {@code PhoneAccount} represents  built-in PSTN SIM subscription.
      * <p>
-     * See {@link #getCapabilities}
-     */
-    public static final int CAPABILITY_SIM_CALL_MANAGER = 0x1;
-
-    /**
-     * Flag indicating that this {@code PhoneAccount} can make phone calls in place of traditional
-     * SIM-based telephony calls. This account will be treated as a distinct method for placing
-     * calls alongside the traditional SIM-based telephony stack. This flag is distinct from
-     * {@link #CAPABILITY_SIM_CALL_MANAGER} in that it is not allowed to manage calls from or use
-     * the built-in telephony stack to place its calls.
-     * <p>
-     * See {@link #getCapabilities}
-     */
-    public static final int CAPABILITY_CALL_PROVIDER = 0x2;
-
-    /**
-     * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM subscription.
-     * <p>
-     * Only the android framework can set this capability on a phone-account.
+     * Only the android framework can set this capability on a phone account.
      */
     public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
 
     private ComponentName mComponentName;
     private String mId;
-    private Uri mHandle;
-    private int mCapabilities;
 
     public PhoneAccount(
             ComponentName componentName,
-            String id,
-            Uri handle,
-            int capabilities) {
+            String id) {
         mComponentName = componentName;
         mId = id;
-        mHandle = handle;
-        mCapabilities = capabilities;
     }
 
     /**
@@ -97,31 +71,9 @@
         return mId;
     }
 
-    /**
-     * The handle (e.g., a phone number) associated with this {@code PhoneAccount}. This represents
-     * the destination from which outgoing calls using this {@code PhoneAccount} will appear to
-     * come, if applicable, and the destination to which incoming calls using this
-     * {@code PhoneAccount} may be addressed.
-     *
-     * @return A handle expressed as a {@code Uri}, for example, a phone number.
-     */
-    public Uri getHandle() {
-        return mHandle;
-    }
-
-    /**
-     * The capabilities of this {@code PhoneAccount}.
-     *
-     * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities.
-     */
-    public int getCapabilities() {
-        return mCapabilities;
-    }
-
     @Override
     public int hashCode() {
-        return Objects.hashCode(mComponentName) + Objects.hashCode(mId) +
-                Objects.hashCode(mHandle) + mCapabilities;
+        return Objects.hashCode(mComponentName) + Objects.hashCode(mId);
     }
 
     @Override
@@ -130,20 +82,16 @@
                     .append(", ")
                     .append(mId)
                     .append(", ")
-                    .append(Log.pii(mHandle))
                     .append(", ")
-                    .append(String.valueOf(mCapabilities))
                     .toString();
     }
 
-    /**
-     * TODO: Change this to just be equals() and use Set<> in Telecomm code instead of Lists.
-     * @hide
-     */
-    public boolean equalsComponentAndId(PhoneAccount other) {
+    @Override
+    public boolean equals(Object other) {
         return other != null &&
-                Objects.equals(other.getComponentName(), getComponentName()) &&
-                Objects.equals(other.getId(), getId());
+                other instanceof PhoneAccount &&
+                Objects.equals(((PhoneAccount) other).getComponentName(), getComponentName()) &&
+                Objects.equals(((PhoneAccount) other).getId(), getId());
     }
 
     //
@@ -159,8 +107,6 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeParcelable(mComponentName, flags);
         out.writeString(mId);
-        out.writeString(mHandle != null ? mHandle.toString() : "");
-        out.writeInt(mCapabilities);
     }
 
     public static final Creator<PhoneAccount> CREATOR = new Creator<PhoneAccount>() {
@@ -178,8 +124,5 @@
     private PhoneAccount(Parcel in) {
         mComponentName = in.readParcelable(getClass().getClassLoader());
         mId = in.readString();
-        String uriString = in.readString();
-        mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null;
-        mCapabilities = in.readInt();
     }
 }
diff --git a/telecomm/java/android/telecomm/PhoneAccountMetadata.java b/telecomm/java/android/telecomm/PhoneAccountMetadata.java
index 20a4d47..e5e41ff 100644
--- a/telecomm/java/android/telecomm/PhoneAccountMetadata.java
+++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.java
@@ -19,32 +19,67 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.io.IOException;
-import java.io.ObjectStreamException;
-import java.io.Serializable;
 import java.util.MissingResourceException;
 
 /**
  * Provides user interface description information for a {@code PhoneAccount}.
+ *
+ * TODO: Per feedback from API Council, rename to "PhoneAccount". See also comment on class
+ * PhoneAccount.
  */
 public class PhoneAccountMetadata implements Parcelable {
-    private PhoneAccount mAccount;
-    private int mIconResId;
-    private String mLabel;
-    private String mShortDescription;
+
+    /**
+     * Flag indicating that this {@code PhoneAccount} can act as a call manager for traditional
+     * SIM-based telephony calls. The {@link ConnectionService} associated with this phone-account
+     * will be allowed to manage SIM-based phone calls including using its own proprietary
+     * phone-call implementation (like VoIP calling) to make calls instead of the telephony stack.
+     * When a user opts to place a call using the SIM-based telephony stack, the connection-service
+     * associated with this phone-account will be attempted first if the user has explicitly
+     * selected it to be used as the default call-manager.
+     * <p>
+     * See {@link #getCapabilities}
+     */
+    public static final int CAPABILITY_SIM_CALL_MANAGER = 0x1;
+
+    /**
+     * Flag indicating that this {@code PhoneAccount} can make phone calls in place of traditional
+     * SIM-based telephony calls. This account will be treated as a distinct method for placing
+     * calls alongside the traditional SIM-based telephony stack. This flag is distinct from
+     * {@link #CAPABILITY_SIM_CALL_MANAGER} in that it is not allowed to manage calls from or use
+     * the built-in telephony stack to place its calls.
+     * <p>
+     * See {@link #getCapabilities}
+     */
+    public static final int CAPABILITY_CALL_PROVIDER = 0x2;
+
+    private final PhoneAccount mAccount;
+    private final Uri mHandle;
+    private final int mCapabilities;
+    private final int mIconResId;
+    private final String mLabel;
+    private final String mShortDescription;
+    private boolean mVideoCallingSupported;
 
     public PhoneAccountMetadata(
             PhoneAccount account,
+            Uri handle,
+            int capabilities,
             int iconResId,
             String label,
-            String shortDescription) {
+            String shortDescription,
+            boolean supportsVideoCalling) {
         mAccount = account;
+        mHandle = handle;
+        mCapabilities = capabilities;
         mIconResId = iconResId;
         mLabel = label;
         mShortDescription = shortDescription;
+        mVideoCallingSupported = supportsVideoCalling;
     }
 
     /**
@@ -57,6 +92,27 @@
     }
 
     /**
+     * The handle (e.g., a phone number) associated with this {@code PhoneAccount}. This represents
+     * the destination from which outgoing calls using this {@code PhoneAccount} will appear to
+     * come, if applicable, and the destination to which incoming calls using this
+     * {@code PhoneAccount} may be addressed.
+     *
+     * @return A handle expressed as a {@code Uri}, for example, a phone number.
+     */
+    public Uri getHandle() {
+        return mHandle;
+    }
+
+    /**
+     * The capabilities of this {@code PhoneAccount}.
+     *
+     * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities.
+     */
+    public int getCapabilities() {
+        return mCapabilities;
+    }
+
+    /**
      * A short string label describing a {@code PhoneAccount}.
      *
      * @return A label for this {@code PhoneAccount}.
@@ -75,6 +131,15 @@
     }
 
     /**
+     * The icon resource ID for the icon of this {@code PhoneAccount}.
+     *
+     * @return A resource ID.
+     */
+    public int getIconResId() {
+        return mIconResId;
+    }
+
+    /**
      * An icon to represent this {@code PhoneAccount} in a user interface.
      *
      * @return An icon for this {@code PhoneAccount}.
@@ -101,6 +166,15 @@
         }
     }
 
+    /**
+     * Determines whether this {@code PhoneAccount} supports video calling.
+     *
+     * @return {@code true} if this {@code PhoneAccount} supports video calling.
+     */
+    public boolean isVideoCallingSupported() {
+        return mVideoCallingSupported;
+    }
+
     //
     // Parcelable implementation
     //
@@ -113,9 +187,12 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeParcelable(mAccount, 0);
+        out.writeParcelable(mHandle, 0);
+        out.writeInt(mCapabilities);
         out.writeInt(mIconResId);
         out.writeString(mLabel);
         out.writeString(mShortDescription);
+        out.writeInt(mVideoCallingSupported ? 1 : 0);
     }
 
     public static final Creator<PhoneAccountMetadata> CREATOR
@@ -133,8 +210,11 @@
 
     private PhoneAccountMetadata(Parcel in) {
         mAccount = in.readParcelable(getClass().getClassLoader());
+        mHandle = in.readParcelable(getClass().getClassLoader());
+        mCapabilities = in.readInt();
         mIconResId = in.readInt();
         mLabel = in.readString();
         mShortDescription = in.readString();
+        mVideoCallingSupported = in.readInt() == 1;
     }
 }
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index 5d8579e..d17e62a 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -42,6 +42,7 @@
         void onHandleChanged(RemoteConnection connection, Uri handle, int presentation);
         void onCallerDisplayNameChanged(
                 RemoteConnection connection, String callerDisplayName, int presentation);
+        void onVideoStateChanged(RemoteConnection connection, int videoState);
         void onDestroyed(RemoteConnection connection);
     }
 
@@ -55,6 +56,7 @@
     private boolean mRequestingRingback;
     private boolean mConnected;
     private int mCallCapabilities;
+    private int mVideoState;
     private boolean mAudioModeIsVoip;
     private StatusHints mStatusHints;
     private Uri mHandle;
@@ -120,6 +122,10 @@
         return mCallerDisplayNamePresentation;
     }
 
+    public int getVideoState() {
+        return mVideoState;
+    }
+
     public void abort() {
         try {
             if (mConnected) {
@@ -297,6 +303,16 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    void setVideoState(int videoState) {
+        mVideoState = videoState;
+        for (Listener l : mListeners) {
+            l.onVideoStateChanged(this, videoState);
+        }
+    }
+
     /** @hide */
     void setAudioModeIsVoip(boolean isVoip) {
         mAudioModeIsVoip = isVoip;
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 7fd8f93..c2b574c 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -167,6 +167,13 @@
         }
 
         @Override
+        public void setVideoState(String connectionId, int videoState) {
+            if (isCurrentConnection(connectionId)) {
+                mConnection.setVideoState(videoState);
+            }
+        }
+
+        @Override
         public final void setAudioModeIsVoip(String connectionId, boolean isVoip) {
             if (isCurrentConnection(connectionId)) {
                 mConnection.setAudioModeIsVoip(isVoip);
@@ -253,9 +260,7 @@
         List<PhoneAccount> accounts = new LinkedList<>();
         accounts.add(new PhoneAccount(
                 mComponentName,
-                null /* id */,
-                null /* handle */,
-                0 /* capabilities */));
+                null /* id */));
         return accounts;
     }
 
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 8bf80bb..89fcdb5 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -56,6 +56,34 @@
     }
 
     /**
+     * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing
+     * phone calls. This {@code PhoneAccount} will always be a member of the list which is
+     * returned from calling {@link #getEnabledPhoneAccounts()}.
+     * <p>
+     * Apps must be prepared for this method to return {@code null}, indicating that there
+     * currently exists no user-chosen default {@code PhoneAccount}. In this case, apps wishing to
+     * initiate a phone call must either create their {@link android.content.Intent#ACTION_CALL} or
+     * {@link android.content.Intent#ACTION_DIAL} {@code Intent} with no
+     * {@link TelecommConstants#EXTRA_PHONE_ACCOUNT}, or present the user with an affordance
+     * to select one of the elements of {@link #getEnabledPhoneAccounts()}.
+     * <p>
+     * An {@link android.content.Intent#ACTION_CALL} or {@link android.content.Intent#ACTION_DIAL}
+     * {@code Intent} with no {@link TelecommConstants#EXTRA_PHONE_ACCOUNT} is valid, and subsequent
+     * steps in the phone call flow are responsible for presenting the user with an affordance, if
+     * necessary, to choose a {@code PhoneAccount}.
+     */
+    public PhoneAccount getDefaultOutgoingPhoneAccount() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().getDefaultOutgoingPhoneAccount();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#getDefaultOutgoingPhoneAccount", e);
+        }
+        return null;
+    }
+
+    /**
      * Return a list of {@link PhoneAccount}s which can be used to make and receive phone calls.
      *
      * @see #EXTRA_PHONE_ACCOUNT
@@ -94,13 +122,12 @@
     /**
      * Register a {@link PhoneAccount} for use by the system.
      *
-     * @param account The {@link PhoneAccount}.
-     * @param metadata The metadata for the account.
+     * @param metadata The complete {@link PhoneAccountMetadata}.
      */
-    public void registerPhoneAccount(PhoneAccount account, PhoneAccountMetadata metadata) {
+    public void registerPhoneAccount(PhoneAccountMetadata metadata) {
         try {
             if (isServiceConnected()) {
-                getTelecommService().registerPhoneAccount(account, metadata);
+                getTelecommService().registerPhoneAccount(metadata);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelecommService#registerPhoneAccount", e);
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index b36f72c..e12cfca 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -64,6 +64,8 @@
 
     void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider);
 
+    void setVideoState(String callId, int videoState);
+
     void setAudioModeIsVoip(String callId, boolean isVoip);
 
     void setStatusHints(String callId, in StatusHints statusHints);
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 43caa1e..59393ed 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -34,6 +34,11 @@
     void showCallScreen(boolean showDialpad);
 
     /**
+     * @see TelecommManager#getDefaultOutgoingPhoneAccount
+     */
+    PhoneAccount getDefaultOutgoingPhoneAccount();
+
+    /**
      * @see TelecommManager#getEnabledPhoneAccounts
      */
     List<PhoneAccount> getEnabledPhoneAccounts();
@@ -46,7 +51,7 @@
     /**
      * @see TelecommManager#registerPhoneAccount
      */
-    void registerPhoneAccount(in PhoneAccount account, in PhoneAccountMetadata metadata);
+    void registerPhoneAccount(in PhoneAccountMetadata metadata);
 
     /**
      * @see TelecommManager#unregisterPhoneAccount
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 208f467..6896f4d 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telecomm.VideoCallProfile;
 
 /**
  * Parcelable object to handle IMS call profile.
@@ -286,4 +287,63 @@
             return new ImsCallProfile[size];
         }
     };
+
+    /**
+     * Converts from the call types defined in {@link com.android.ims.ImsCallProfile} to the
+     * video state values defined in {@link android.telecomm.VideoCallProfile}.
+     *
+     * @param callType The call type.
+     * @return The video state.
+     */
+    public static int getVideoStateFromCallType(int callType) {
+        switch (callType) {
+            case CALL_TYPE_VT_NODIR:
+                return VideoCallProfile.VIDEO_STATE_PAUSED |
+                        VideoCallProfile.VIDEO_STATE_BIDIRECTIONAL;
+            case CALL_TYPE_VT_TX:
+                return VideoCallProfile.VIDEO_STATE_TX_ENABLED;
+            case CALL_TYPE_VT_RX:
+                return VideoCallProfile.VIDEO_STATE_RX_ENABLED;
+            case CALL_TYPE_VT:
+                return VideoCallProfile.VIDEO_STATE_BIDIRECTIONAL;
+            case CALL_TYPE_VOICE:
+                return VideoCallProfile.VIDEO_STATE_AUDIO_ONLY;
+            default:
+                return VideoCallProfile.VIDEO_STATE_AUDIO_ONLY;
+        }
+    }
+
+    /**
+     * Converts from the video state values defined in {@link android.telecomm.VideoCallProfile}
+     * to the call types defined in {@link ImsCallProfile}.
+     *
+     * @param videoState The video state.
+     * @return The call type.
+     */
+    public static int getCallTypeFromVideoState(int videoState) {
+        boolean videoTx = isVideoStateSet(videoState, VideoCallProfile.VIDEO_STATE_TX_ENABLED);
+        boolean videoRx = isVideoStateSet(videoState, VideoCallProfile.VIDEO_STATE_RX_ENABLED);
+        boolean isPaused = isVideoStateSet(videoState, VideoCallProfile.VIDEO_STATE_PAUSED);
+        if (isPaused) {
+            return ImsCallProfile.CALL_TYPE_VT_NODIR;
+        } else if (videoTx && !videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT_TX;
+        } else if (!videoTx && videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT_RX;
+        } else if (videoTx && videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT;
+        }
+        return ImsCallProfile.CALL_TYPE_VOICE;
+    }
+
+    /**
+     * Determines if a video state is set in a video state bit-mask.
+     *
+     * @param videoState The video state bit mask.
+     * @param videoStateToCheck The particular video state to check.
+     * @return True if the video state is set in the bit-mask.
+     */
+    private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
+        return (videoState & videoStateToCheck) == videoStateToCheck;
+    }
 }
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index ab2e8ac..edb28ea 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -18,6 +18,8 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.service.voice.AlwaysOnHotwordDetector;
+import android.service.voice.AlwaysOnHotwordDetector.Callback;
 import android.service.voice.VoiceInteractionService;
 import android.util.Log;
 
@@ -26,6 +28,25 @@
 public class MainInteractionService extends VoiceInteractionService {
     static final String TAG = "MainInteractionService";
 
+    private final Callback mHotwordCallback = new Callback() {
+        @Override
+        public void onDetected(byte[] data) {
+            Log.i(TAG, "onDetected");
+        }
+
+        @Override
+        public void onDetectionStarted() {
+            Log.i(TAG, "onDetectionStarted");
+        }
+
+        @Override
+        public void onDetectionStopped() {
+            Log.i(TAG, "onDetectionStopped");
+        }
+    };
+
+    private AlwaysOnHotwordDetector mHotwordDetector;
+
     @Override
     public void onReady() {
         super.onReady();
@@ -33,6 +54,31 @@
         Log.i(TAG, "Keyphrase enrollment error? " + getKeyphraseEnrollmentInfo().getParseError());
         Log.i(TAG, "Keyphrase enrollment meta-data: "
                 + Arrays.toString(getKeyphraseEnrollmentInfo().listKeyphraseMetadata()));
+
+        mHotwordDetector = getAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
+        int availability = mHotwordDetector.getAvailability();
+        Log.i(TAG, "Hotword availability = " + availability);
+
+        switch (availability) {
+            case AlwaysOnHotwordDetector.KEYPHRASE_HARDWARE_UNAVAILABLE:
+                Log.i(TAG, "KEYPHRASE_HARDWARE_UNAVAILABLE");
+                break;
+            case AlwaysOnHotwordDetector.KEYPHRASE_UNSUPPORTED:
+                Log.i(TAG, "KEYPHRASE_UNSUPPORTED");
+                break;
+            case AlwaysOnHotwordDetector.KEYPHRASE_UNENROLLED:
+                Log.i(TAG, "KEYPHRASE_UNENROLLED");
+                Intent enroll = mHotwordDetector.getManageIntent(
+                        AlwaysOnHotwordDetector.MANAGE_ACTION_ENROLL);
+                Log.i(TAG, "Need to enroll with " + enroll);
+                break;
+            case AlwaysOnHotwordDetector.KEYPHRASE_ENROLLED:
+                Log.i(TAG, "KEYPHRASE_ENROLLED");
+                int status = mHotwordDetector.startRecognition(
+                        AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE);
+                Log.i(TAG, "startRecognition status = " + status);
+                break;
+        }
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java b/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
index a953918..93814b2 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
@@ -35,7 +35,7 @@
             // Note that AssetManager() creates a system AssetManager and we override it
             // with our BridgeAssetManager.
             AssetManager.sSystem = new BridgeAssetManager();
-            AssetManager.sSystem.makeStringBlocks(false);
+            AssetManager.sSystem.makeStringBlocks(null);
         }
         return AssetManager.sSystem;
     }