Merge "[HWUI]: fix residual line on FrameBuffer" into lmp-dev
diff --git a/Android.mk b/Android.mk
index 9cda341..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 \
@@ -241,6 +242,7 @@
 	core/java/android/view/IWindowManager.aidl \
 	core/java/android/view/IWindowSession.aidl \
 	core/java/android/view/IWindowSessionCallback.aidl \
+	core/java/android/webkit/IWebViewUpdateService.aidl \
 	core/java/android/speech/IRecognitionListener.aidl \
 	core/java/android/speech/IRecognitionService.aidl \
 	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
@@ -324,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 87ea82e..2204fbb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -299,6 +299,9 @@
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
     field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
     field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
+    field public static final int amPmSelectedBackgroundColor = 16843947; // 0x10104ab
+    field public static final int amPmTextColor = 16843945; // 0x10104a9
+    field public static final int amPmUnselectedBackgroundColor = 16843946; // 0x10104aa
     field public static final int angle = 16843168; // 0x10101a0
     field public static final int animateFirstView = 16843477; // 0x10102d5
     field public static final int animateLayoutChanges = 16843506; // 0x10102f2
@@ -626,8 +629,12 @@
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
     field public static final int hardwareAccelerated = 16843475; // 0x10102d3
     field public static final int hasCode = 16842764; // 0x101000c
+    field public static final int headerAmPmTextAppearance = 16843939; // 0x10104a3
     field public static final int headerBackground = 16843055; // 0x101012f
+    field public static final int headerBackgroundColor = 16843941; // 0x10104a5
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
+    field public static final int headerSelectedTextColor = 16843940; // 0x10104a4
+    field public static final int headerTimeTextAppearance = 16843938; // 0x10104a2
     field public static final int height = 16843093; // 0x1010155
     field public static final int hideOnContentScroll = 16843845; // 0x1010445
     field public static final int hint = 16843088; // 0x1010150
@@ -882,6 +889,9 @@
     field public static final int notificationTimeout = 16843651; // 0x1010383
     field public static final int numColumns = 16843032; // 0x1010118
     field public static final int numStars = 16843076; // 0x1010144
+    field public static final int numbersBackgroundColor = 16843943; // 0x10104a7
+    field public static final int numbersSelectorColor = 16843944; // 0x10104a8
+    field public static final int numbersTextColor = 16843942; // 0x10104a6
     field public static final deprecated int numeric = 16843109; // 0x1010165
     field public static final int numericShortcut = 16843236; // 0x10101e4
     field public static final int onClick = 16843375; // 0x101026f
@@ -986,6 +996,7 @@
     field public static final int ratingBarStyleIndicator = 16843280; // 0x1010210
     field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
     field public static final int readPermission = 16842759; // 0x1010007
+    field public static final int recognitionService = 16843935; // 0x101049f
     field public static final int relinquishTaskIdentity = 16843896; // 0x1010478
     field public static final int repeatCount = 16843199; // 0x10101bf
     field public static final int repeatMode = 16843200; // 0x10101c0
@@ -1051,6 +1062,7 @@
     field public static final int searchIcon = 16843909; // 0x1010485
     field public static final int searchKeyphrase = 16843873; // 0x1010461
     field public static final int searchKeyphraseId = 16843872; // 0x1010460
+    field public static final int searchKeyphraseRecognitionFlags = 16843948; // 0x10104ac
     field public static final int searchKeyphraseSupportedLocales = 16843874; // 0x1010462
     field public static final int searchMode = 16843221; // 0x10101d5
     field public static final int searchSettingsDescription = 16843402; // 0x101028a
@@ -1275,6 +1287,8 @@
     field public static final int tileMode = 16843265; // 0x1010201
     field public static final int tileModeX = 16843897; // 0x1010479
     field public static final int tileModeY = 16843898; // 0x101047a
+    field public static final int timePickerDialogTheme = 16843937; // 0x10104a1
+    field public static final int timePickerStyle = 16843936; // 0x10104a0
     field public static final int timeZone = 16843724; // 0x10103cc
     field public static final int tint = 16843041; // 0x1010121
     field public static final int tintMode = 16843797; // 0x1010415
@@ -3600,6 +3614,7 @@
     field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
     field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
     field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
+    field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
     field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
     field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
     field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
@@ -5312,6 +5327,7 @@
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public boolean getBlockUninstall(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5351,6 +5367,7 @@
     method public int setApplicationsBlocked(android.content.ComponentName, android.content.Intent, boolean);
     method public void setBlockUninstall(android.content.ComponentName, java.lang.String, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(java.lang.String[]) throws java.lang.SecurityException;
@@ -5437,6 +5454,7 @@
     method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
     method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
     method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
+    method public void onRestoreFinished();
     field public static final int TYPE_DIRECTORY = 2; // 0x2
     field public static final int TYPE_FILE = 1; // 0x1
   }
@@ -7173,6 +7191,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
@@ -8666,9 +8685,13 @@
     field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
     field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
     field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
+    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
+    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
+    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
     field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
     field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
+    field public static final java.lang.String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
@@ -12746,9 +12769,9 @@
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
-    field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -13085,6 +13108,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);
@@ -13094,6 +13118,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
   }
 
@@ -13110,6 +13135,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 {
@@ -15986,6 +16018,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 {
@@ -16407,8 +16461,8 @@
     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 buildChannelsUriForInput(android.content.ComponentName);
-    method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
+    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 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);
@@ -16428,12 +16482,12 @@
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
     field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_INPUT_ID = "input_id";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
     field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
     field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
-    field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
     field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
     field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
     field public static final java.lang.String COLUMN_TYPE = "type";
@@ -16535,12 +16589,13 @@
     method public java.lang.String getId();
     method public android.content.Intent getIntentForSettingsActivity();
     method public android.content.Intent getIntentForSetupActivity();
+    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 void writeToParcel(android.os.Parcel, int);
-    field public static final java.lang.String EXTRA_SERVICE_NAME = "serviceName";
+    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_TUNER = 2; // 0x2
@@ -22292,9 +22347,10 @@
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
     field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
     field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
+    field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
-    field public static final java.lang.String DISALLOW_TELEPHONY = "no_telephony";
+    field public static final java.lang.String DISALLOW_SMS = "no_sms";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -26945,7 +27001,8 @@
     method public int getAvailability();
     method public android.content.Intent getManageIntent(int);
     method public int getRecognitionStatus();
-    method public int startRecognition();
+    method public int getSupportedRecognitionModes();
+    method public int startRecognition(int);
     method public int stopRecognition();
     field public static final int KEYPHRASE_ENROLLED = 2; // 0x2
     field public static final int KEYPHRASE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
@@ -26954,17 +27011,21 @@
     field public static final int MANAGE_ACTION_ENROLL = 0; // 0x0
     field public static final int MANAGE_ACTION_RE_ENROLL = 1; // 0x1
     field public static final int MANAGE_ACTION_UN_ENROLL = 2; // 0x2
-    field public static final int RECOGNITION_ACTIVE = 2; // 0x2
-    field public static final int RECOGNITION_DISABLED_TEMPORARILY = -1; // 0xffffffff
-    field public static final int RECOGNITION_NOT_AVAILABLE = -3; // 0xfffffffd
-    field public static final int RECOGNITION_NOT_REQUESTED = -2; // 0xfffffffe
-    field public static final int RECOGNITION_REQUESTED = 1; // 0x1
+    field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
+    field public static final int RECOGNITION_FLAG_NONE = 0; // 0x0
+    field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
+    field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
+    field public static final int RECOGNITION_STATUS_ACTIVE = 16; // 0x10
+    field public static final int RECOGNITION_STATUS_DISABLED_TEMPORARILY = 8; // 0x8
+    field public static final int RECOGNITION_STATUS_NOT_AVAILABLE = 1; // 0x1
+    field public static final int RECOGNITION_STATUS_NOT_REQUESTED = 2; // 0x2
+    field public static final int RECOGNITION_STATUS_REQUESTED = 4; // 0x4
     field public static final int STATUS_ERROR = -2147483648; // 0x80000000
     field public static final int STATUS_OK = 1; // 0x1
   }
 
   public static abstract interface AlwaysOnHotwordDetector.Callback {
-    method public abstract void onDetected();
+    method public abstract void onDetected(byte[]);
     method public abstract void onDetectionStarted();
     method public abstract void onDetectionStopped();
   }
@@ -26972,7 +27033,9 @@
   public class VoiceInteractionService extends android.app.Service {
     ctor public VoiceInteractionService();
     method public final android.service.voice.AlwaysOnHotwordDetector getAlwaysOnHotwordDetector(java.lang.String, java.lang.String, android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onReady();
     method public void startSession(android.os.Bundle);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
@@ -27231,6 +27294,7 @@
     method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
     method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int setAudioAttributes(android.media.AudioAttributes);
     method public deprecated int setEngineByPackageName(java.lang.String);
     method public int setLanguage(java.util.Locale);
     method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
@@ -27995,7 +28059,7 @@
 
   public final class Call {
     method public void addListener(android.telecomm.Call.Listener);
-    method public void answer();
+    method public void answer(int);
     method public void conference();
     method public void disconnect();
     method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider();
@@ -28007,6 +28071,7 @@
     method public int getState();
     method public void hold();
     method public void phoneAccountClicked();
+    method public void phoneAccountSelected(android.telecomm.PhoneAccount);
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void reject(boolean, java.lang.String);
@@ -28020,6 +28085,7 @@
     field public static final int STATE_DISCONNECTED = 7; // 0x7
     field public static final int STATE_HOLDING = 3; // 0x3
     field public static final int STATE_NEW = 0; // 0x0
+    field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
     field public static final int STATE_RINGING = 2; // 0x2
   }
 
@@ -28034,6 +28100,7 @@
     method public android.telecomm.GatewayInfo getGatewayInfo();
     method public android.net.Uri getHandle();
     method public int getHandlePresentation();
+    method public android.telecomm.StatusHints getStatusHints();
     method public int getVideoState();
   }
 
@@ -28107,6 +28174,7 @@
     enum_constant public static final android.telecomm.CallState DISCONNECTED;
     enum_constant public static final android.telecomm.CallState NEW;
     enum_constant public static final android.telecomm.CallState ON_HOLD;
+    enum_constant public static final android.telecomm.CallState PRE_DIAL_WAIT;
     enum_constant public static final android.telecomm.CallState RINGING;
   }
 
@@ -28159,7 +28227,7 @@
     method public final boolean isConferenceConnection();
     method public final boolean isRequestingRingback();
     method protected void onAbort();
-    method protected void onAnswer();
+    method protected void onAnswer(int);
     method protected void onChildrenChanged(java.util.List<android.telecomm.Connection>);
     method protected void onDisconnect();
     method protected void onHold();
@@ -28246,11 +28314,12 @@
   }
 
   public final class InCallAdapter {
-    method public void answerCall(java.lang.String);
+    method public void answerCall(java.lang.String, int);
     method public void disconnectCall(java.lang.String);
     method public void holdCall(java.lang.String);
     method public void mute(boolean);
     method public void phoneAccountClicked(java.lang.String);
+    method public void phoneAccountSelected(java.lang.String, android.telecomm.PhoneAccount);
     method public void playDtmfTone(java.lang.String, char);
     method public void postDialContinue(java.lang.String, boolean);
     method public void rejectCall(java.lang.String, boolean, java.lang.String);
@@ -28359,7 +28428,7 @@
   public final class RemoteConnection {
     method public void abort();
     method public void addListener(android.telecomm.RemoteConnection.Listener);
-    method public void answer();
+    method public void answer(int);
     method public void disconnect();
     method public boolean getAudioModeIsVoip();
     method public int getCallCapabilities();
@@ -28427,6 +28496,7 @@
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
+    field public static final java.lang.String EXTRA_EMERGENCY_CALL_BACK_NUMBER = "android.telecomm.extra.EMERGENCY_CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT = "android.intent.extra.PHONE_ACCOUNT";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -31140,6 +31210,36 @@
     field public static final int WEEKDAY_WEDNESDAY = 4; // 0x4
   }
 
+  public static abstract class TtsSpan.Builder {
+    ctor public TtsSpan.Builder(java.lang.String);
+    method public android.text.style.TtsSpan build();
+    method public C setIntArgument(java.lang.String, int);
+    method public C setLongArgument(java.lang.String, long);
+    method public C setStringArgument(java.lang.String, java.lang.String);
+  }
+
+  public static class TtsSpan.CardinalBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.CardinalBuilder();
+    ctor public TtsSpan.CardinalBuilder(long);
+    ctor public TtsSpan.CardinalBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.CardinalBuilder setNumber(long);
+    method public android.text.style.TtsSpan.CardinalBuilder 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);
+    method public C setCase(java.lang.String);
+    method public C setGender(java.lang.String);
+    method public C setMultiplicity(java.lang.String);
+  }
+
+  public static class TtsSpan.TextBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.TextBuilder();
+    ctor public TtsSpan.TextBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.TextBuilder setText(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);
@@ -33463,6 +33563,7 @@
     method public boolean dispatchKeyEventPreIme(android.view.KeyEvent);
     method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
     method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
@@ -34273,6 +34374,7 @@
     method public boolean onInterceptTouchEvent(android.view.MotionEvent);
     method protected abstract void onLayout(boolean, int, int, int, int);
     method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
     method public void onNestedPreScroll(android.view.View, int, int, int[]);
     method public void onNestedScroll(android.view.View, int, int, int, int);
     method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
@@ -34418,6 +34520,7 @@
     method public abstract boolean isTextDirectionResolved();
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
     method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
     method public abstract void onNestedScroll(android.view.View, int, int, int, int);
     method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 806a55b..aab6e80 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -155,6 +155,8 @@
 
     /** @hide */
     public static final int ERROR_CODE_USER_RESTRICTED = 100;
+    /** @hide */
+    public static final int ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101;
 
     /**
      * Bundle key used for the {@link String} account name in results
@@ -678,8 +680,7 @@
      * @param handler {@link Handler} identifying the callback thread,
      *     null for the main thread
      * @return An {@link AccountManagerFuture} which resolves to a Boolean,
-     *     true if the account has been successfully removed,
-     *     false if the authenticator forbids deleting this account.
+     *     true if the account has been successfully removed
      */
     public AccountManagerFuture<Boolean> removeAccount(final Account account,
             AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -698,6 +699,28 @@
     }
 
     /**
+     * @see #removeAccount(Account, AccountManagerCallback, Handler)
+     * @hide
+     */
+    public AccountManagerFuture<Boolean> removeAccountAsUser(final Account account,
+            AccountManagerCallback<Boolean> callback, Handler handler,
+            final UserHandle userHandle) {
+        if (account == null) throw new IllegalArgumentException("account is null");
+        if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
+        return new Future2Task<Boolean>(handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.removeAccountAsUser(mResponse, account, userHandle.getIdentifier());
+            }
+            public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+                if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
+                    throw new AuthenticatorException("no result in response");
+                }
+                return bundle.getBoolean(KEY_BOOLEAN_RESULT);
+            }
+        }.start();
+    }
+
+    /**
      * Removes an auth token from the AccountManager's cache.  Does nothing if
      * the auth token is not currently in the cache.  Applications must call this
      * method when the auth token is found to have expired or otherwise become
@@ -1183,7 +1206,8 @@
      * <li> {@link AuthenticatorException} if no authenticator was registered for
      *      this account type or the authenticator failed to respond
      * <li> {@link OperationCanceledException} if the operation was canceled for
-     *      any reason, including the user canceling the creation process
+     *      any reason, including the user canceling the creation process or adding accounts
+     *      (of this type) has been disabled by policy
      * <li> {@link IOException} if the authenticator experienced an I/O problem
      *      creating a new account, usually because of network trouble
      * </ul>
@@ -1208,6 +1232,30 @@
     }
 
     /**
+     * @see #addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback, Handler)
+     * @hide
+     */
+    public AccountManagerFuture<Bundle> addAccountAsUser(final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final Bundle addAccountOptions, final Activity activity,
+            AccountManagerCallback<Bundle> callback, Handler handler, final UserHandle userHandle) {
+        if (accountType == null) throw new IllegalArgumentException("accountType is null");
+        if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
+        final Bundle optionsIn = new Bundle();
+        if (addAccountOptions != null) {
+            optionsIn.putAll(addAccountOptions);
+        }
+        optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.addAccountAsUser(mResponse, accountType, authTokenType,
+                        requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier());
+            }
+        }.start();
+    }
+
+    /**
      * Adds a shared account from the primary user to a secondary user. Adding the shared account
      * doesn't take effect immediately. When the target user starts up, any pending shared accounts
      * are attempted to be copied to the target user from the primary via calls to the
@@ -1608,8 +1656,10 @@
             }
 
             public void onError(int code, String message) {
-                if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED) {
-                    // the authenticator indicated that this request was canceled, do so now
+                if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
+                        || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
+                    // the authenticator indicated that this request was canceled or we were
+                    // forbidden to fulfill; cancel now
                     cancel(true /* mayInterruptIfRunning */);
                     return;
                 }
@@ -1668,7 +1718,10 @@
             }
 
             public void onError(int code, String message) {
-                if (code == ERROR_CODE_CANCELED) {
+                if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
+                        || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
+                    // the authenticator indicated that this request was canceled or we were
+                    // forbidden to fulfill; cancel now
                     cancel(true /* mayInterruptIfRunning */);
                     return;
                 }
diff --git a/core/java/android/accounts/AccountManagerFuture.java b/core/java/android/accounts/AccountManagerFuture.java
index af00a08..77670d9 100644
--- a/core/java/android/accounts/AccountManagerFuture.java
+++ b/core/java/android/accounts/AccountManagerFuture.java
@@ -84,7 +84,8 @@
      * will be thrown rather than the call returning normally.
      * @return the actual result
      * @throws android.accounts.OperationCanceledException if the request was canceled for any
-     * reason
+     * reason (including if it is forbidden
+     * by policy to modify an account (of that type))
      * @throws android.accounts.AuthenticatorException if there was an error communicating with
      * the authenticator or if the authenticator returned an invalid response
      * @throws java.io.IOException if the authenticator returned an error response that indicates
diff --git a/core/java/android/accounts/CantAddAccountActivity.java b/core/java/android/accounts/CantAddAccountActivity.java
index e1717a6..4ac2beb 100644
--- a/core/java/android/accounts/CantAddAccountActivity.java
+++ b/core/java/android/accounts/CantAddAccountActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
+import android.widget.TextView;
 
 import com.android.internal.R;
 
@@ -27,11 +28,26 @@
  * Just shows an error message about the account restrictions for the limited user.
  */
 public class CantAddAccountActivity extends Activity {
+    public static final String EXTRA_ERROR_CODE = "android.accounts.extra.ERROR_CODE";
+    public static final int MISSING = -1;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.app_not_authorized);
+
+        int errorCode = getIntent().getIntExtra(EXTRA_ERROR_CODE, MISSING);
+        if (errorCode != MISSING) {
+            TextView errorText = (TextView) findViewById(R.id.description);
+            switch (errorCode) {
+                case AccountManager.ERROR_CODE_USER_RESTRICTED:
+                    errorText.setText(R.string.app_no_restricted_accounts);
+                    break;
+                default:
+                    // TODO: Get better message. See: http://b/14642886
+                    errorText.setText(R.string.error_message_title);
+            }
+        }
     }
 
     public void onCancelButtonClicked(View view) {
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 1373dc8..a04875d 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -38,6 +38,7 @@
     void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
     boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
     void removeAccount(in IAccountManagerResponse response, in Account account);
+    void removeAccountAsUser(in IAccountManagerResponse response, in Account account, int userId);
     void invalidateAuthToken(String accountType, String authToken);
     String peekAuthToken(in Account account, String authTokenType);
     void setAuthToken(in Account account, String authTokenType, String authToken);
@@ -52,6 +53,9 @@
     void addAccount(in IAccountManagerResponse response, String accountType,
         String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
         in Bundle options);
+    void addAccountAsUser(in IAccountManagerResponse response, String accountType,
+        String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+        in Bundle options, int userId);
     void updateCredentials(in IAccountManagerResponse response, in Account account,
         String authTokenType, boolean expectActivityLaunch, in Bundle options);
     void editProperties(in IAccountManagerResponse response, String accountType,
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c8cab6f..d2540f1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1819,7 +1819,29 @@
          * actively running code.
          */
         public static final int IMPORTANCE_EMPTY = 500;
-        
+
+        /**
+         * Constant for {@link #importance}: this process does not exist.
+         */
+        public static final int IMPORTANCE_GONE = 1000;
+
+        /** @hide */
+        public static int procStateToImportance(int procState) {
+            if (procState >= ActivityManager.PROCESS_STATE_HOME) {
+                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+            } else if (procState >= ActivityManager.PROCESS_STATE_SERVICE) {
+                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+            } else if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
+            } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
+            } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+            } else {
+                return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+            }
+        }
+
         /**
          * The relative importance level that the system places on this
          * process.  May be one of {@link #IMPORTANCE_FOREGROUND},
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e2b5a84..fb70098 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1465,7 +1465,15 @@
             reply.writeNoException();
             return true;
         }
-        
+
+        case ADD_PACKAGE_DEPENDENCY_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String packageName = data.readString();
+            addPackageDependency(packageName);
+            reply.writeNoException();
+            return true;
+        }
+
         case KILL_APPLICATION_WITH_APPID_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String pkg = data.readString();
@@ -1475,7 +1483,7 @@
             reply.writeNoException();
             return true;
         }
-        
+
         case CLOSE_SYSTEM_DIALOGS_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String reason = data.readString();
@@ -4050,7 +4058,18 @@
         reply.recycle();
         data.recycle();
     }
-    
+
+    public void addPackageDependency(String packageName) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        mRemote.transact(ADD_PACKAGE_DEPENDENCY_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     public void killApplicationWithAppId(String pkg, int appid, String reason)
             throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7c8c83a..48954f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1684,6 +1684,7 @@
                 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
                         ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
                         : true);
+        boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0;
         if ((flags&(Context.CONTEXT_INCLUDE_CODE
                 |Context.CONTEXT_IGNORE_SECURITY))
                 == Context.CONTEXT_INCLUDE_CODE) {
@@ -1698,12 +1699,13 @@
                 throw new SecurityException(msg);
             }
         }
-        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode);
+        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
+                registerPackage);
     }
 
     public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
-        return getPackageInfo(ai, compatInfo, null, false, true);
+        return getPackageInfo(ai, compatInfo, null, false, true, false);
     }
 
     public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
@@ -1719,7 +1721,8 @@
     }
 
     private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
-            ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
+            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
+            boolean registerPackage) {
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
@@ -1738,7 +1741,7 @@
                 packageInfo =
                     new LoadedApk(this, aInfo, compatInfo, baseLoader,
                             securityViolation, includeCode &&
-                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
+                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
                 if (includeCode) {
                     mPackages.put(aInfo.packageName,
                             new WeakReference<LoadedApk>(packageInfo));
@@ -2607,15 +2610,7 @@
             return;
         }
 
-        if (mBackupAgents.get(packageName) != null) {
-            Slog.d(TAG, "BackupAgent " + "  for " + packageName
-                    + " already exists");
-            return;
-        }
-
-        BackupAgent agent = null;
         String classname = data.appInfo.backupAgentName;
-
         // full backup operation but no app-supplied agent?  use the default implementation
         if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
                 || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
@@ -2624,29 +2619,38 @@
 
         try {
             IBinder binder = null;
-            try {
-                if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
-
-                java.lang.ClassLoader cl = packageInfo.getClassLoader();
-                agent = (BackupAgent) cl.loadClass(classname).newInstance();
-
-                // set up the agent's context
-                ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
-                context.setOuterContext(agent);
-                agent.attach(context);
-
-                agent.onCreate();
-                binder = agent.onBind();
-                mBackupAgents.put(packageName, agent);
-            } catch (Exception e) {
-                // If this is during restore, fail silently; otherwise go
-                // ahead and let the user see the crash.
-                Slog.e(TAG, "Agent threw during creation: " + e);
-                if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
-                        && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
-                    throw e;
+            BackupAgent agent = mBackupAgents.get(packageName);
+            if (agent != null) {
+                // reusing the existing instance
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "Reusing existing agent instance");
                 }
-                // falling through with 'binder' still null
+                binder = agent.onBind();
+            } else {
+                try {
+                    if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+
+                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
+                    agent = (BackupAgent) cl.loadClass(classname).newInstance();
+
+                    // set up the agent's context
+                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+                    context.setOuterContext(agent);
+                    agent.attach(context);
+
+                    agent.onCreate();
+                    binder = agent.onBind();
+                    mBackupAgents.put(packageName, agent);
+                } catch (Exception e) {
+                    // If this is during restore, fail silently; otherwise go
+                    // ahead and let the user see the crash.
+                    Slog.e(TAG, "Agent threw during creation: " + e);
+                    if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
+                            && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
+                        throw e;
+                    }
+                    // falling through with 'binder' still null
+                }
             }
 
             // tell the OS that we're live now
@@ -4394,7 +4398,7 @@
             instrApp.dataDir = ii.dataDir;
             instrApp.nativeLibraryDir = ii.nativeLibraryDir;
             LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
-                    appContext.getClassLoader(), false, true);
+                    appContext.getClassLoader(), false, true, false);
             ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
 
             try {
diff --git a/core/java/android/app/AppImportanceMonitor.java b/core/java/android/app/AppImportanceMonitor.java
new file mode 100644
index 0000000..c760e1e
--- /dev/null
+++ b/core/java/android/app/AppImportanceMonitor.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import java.util.List;
+
+/**
+ * Helper for monitoring the current importance of applications.
+ * @hide
+ */
+public class AppImportanceMonitor {
+    final Context mContext;
+
+    final SparseArray<AppEntry> mApps = new SparseArray<>();
+
+    static class AppEntry {
+        final int uid;
+        final SparseArray<Integer> procs = new SparseArray<>(1);
+        int importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
+
+        AppEntry(int _uid) {
+            uid = _uid;
+        }
+    }
+
+    final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+        @Override
+        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+        }
+
+        @Override
+        public void onProcessStateChanged(int pid, int uid, int procState) {
+            synchronized (mApps) {
+                updateImportanceLocked(pid, uid,
+                        ActivityManager.RunningAppProcessInfo.procStateToImportance(procState),
+                        true);
+            }
+        }
+
+        @Override
+        public void onProcessDied(int pid, int uid) {
+            synchronized (mApps) {
+                updateImportanceLocked(pid, uid,
+                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, true);
+            }
+        }
+    };
+
+    static final int MSG_UPDATE = 1;
+
+    final Handler mHandler;
+
+    public AppImportanceMonitor(Context context, Looper looper) {
+        mContext = context;
+        mHandler = new Handler(looper) {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_UPDATE:
+                        onImportanceChanged(msg.arg1, msg.arg2&0xffff, msg.arg2>>16);
+                        break;
+                    default:
+                        super.handleMessage(msg);
+                }
+            }
+        };
+        ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
+        try {
+            ActivityManagerNative.getDefault().registerProcessObserver(mProcessObserver);
+        } catch (RemoteException e) {
+        }
+        List<ActivityManager.RunningAppProcessInfo> apps = am.getRunningAppProcesses();
+        if (apps != null) {
+            for (int i=0; i<apps.size(); i++) {
+                ActivityManager.RunningAppProcessInfo app = apps.get(i);
+                updateImportanceLocked(app.uid, app.pid, app.importance, false);
+            }
+        }
+    }
+
+    public int getImportance(int uid) {
+        AppEntry ent = mApps.get(uid);
+        if (ent == null) {
+            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
+        }
+        return ent.importance;
+    }
+
+    /**
+     * Report when an app's importance changed. Called on looper given to constructor.
+     */
+    public void onImportanceChanged(int uid, int importance, int oldImportance) {
+    }
+
+    void updateImportanceLocked(int uid, int pid, int importance, boolean repChange) {
+        AppEntry ent = mApps.get(uid);
+        if (ent == null) {
+            ent = new AppEntry(uid);
+            mApps.put(uid, ent);
+        }
+        if (importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
+            ent.procs.remove(pid);
+        } else {
+            ent.procs.put(pid, importance);
+        }
+        updateImportanceLocked(ent, repChange);
+    }
+
+    void updateImportanceLocked(AppEntry ent, boolean repChange) {
+        int appImp = ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
+        for (int i=0; i<ent.procs.size(); i++) {
+            int procImp = ent.procs.valueAt(i);
+            if (procImp < appImp) {
+                appImp = procImp;
+            }
+        }
+        if (appImp != ent.importance) {
+            int impCode = appImp | (ent.importance<<16);
+            ent.importance = appImp;
+            if (appImp >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
+                mApps.remove(ent.uid);
+            }
+            if (repChange) {
+                mHandler.obtainMessage(MSG_UPDATE, ent.uid, impCode).sendToTarget();
+            }
+        }
+    }
+}
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 d7c384a..ee6cdb7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -72,6 +72,7 @@
 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.TvInputManager;
@@ -752,6 +753,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) {
@@ -2097,7 +2104,7 @@
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
-                flags, user.getIdentifier());
+                flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
             ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
                     user, restricted, mDisplay, mOverrideConfiguration);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c6921a2..ac29161 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -294,7 +294,9 @@
     
     public void stopAppSwitches() throws RemoteException;
     public void resumeAppSwitches() throws RemoteException;
-    
+
+    public void addPackageDependency(String packageName) throws RemoteException;
+
     public void killApplicationWithAppId(String pkg, int appid, String reason)
             throws RemoteException;
     
@@ -640,7 +642,7 @@
     int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
     int GET_UID_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
     int HANDLE_INCOMING_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
-    int ___AVAILABLE_2___ = IBinder.FIRST_CALL_TRANSACTION+94;
+    int ADD_PACKAGE_DEPENDENCY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
     int KILL_APPLICATION_WITH_APPID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
     int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
     int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 7036aea..451af99 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -125,6 +125,21 @@
             int token, IBackupManager callbackBinder);
 
     /**
+     * Provide the app with a canonical "all data has been delivered" end-of-restore
+     * callback so that it can do any postprocessing of the restored data that might
+     * be appropriate.  This is issued after both key/value and full data restore
+     * operations have completed.
+     *
+     * @param token Opaque token identifying this transaction.  This must
+     *        be echoed back to the backup service binder once the agent is
+     *        finished restoring the application based on the restore data
+     *        contents.
+     * @param callbackBinder Binder on which to indicate operation completion,
+     *        passed here as a convenience to the agent.
+     */
+    void doRestoreFinished(int token, IBackupManager callbackBinder);
+
+    /**
      * Out of band: instruct the agent to crash within the client process.  This is used
      * when the backup infrastructure detects a semantic error post-hoc and needs to
      * pass the problem back to the app.
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 38614a0..24c2835 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -93,6 +93,7 @@
     private final ClassLoader mBaseClassLoader;
     private final boolean mSecurityViolation;
     private final boolean mIncludeCode;
+    private final boolean mRegisterPackage;
     private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
     Resources mResources;
     private ClassLoader mClassLoader;
@@ -121,7 +122,7 @@
      */
     public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
             CompatibilityInfo compatInfo, ClassLoader baseLoader,
-            boolean securityViolation, boolean includeCode) {
+            boolean securityViolation, boolean includeCode, boolean registerPackage) {
         final int myUid = Process.myUid();
         aInfo = adjustNativeLibraryPaths(aInfo);
 
@@ -144,6 +145,7 @@
         mBaseClassLoader = baseLoader;
         mSecurityViolation = securityViolation;
         mIncludeCode = includeCode;
+        mRegisterPackage = registerPackage;
         mDisplayAdjustments.setCompatibilityInfo(compatInfo);
     }
 
@@ -189,6 +191,7 @@
         mBaseClassLoader = null;
         mSecurityViolation = false;
         mIncludeCode = true;
+        mRegisterPackage = false;
         mClassLoader = ClassLoader.getSystemClassLoader();
         mResources = Resources.getSystem();
     }
@@ -272,6 +275,13 @@
                 final ArrayList<String> zipPaths = new ArrayList<>();
                 final ArrayList<String> libPaths = new ArrayList<>();
 
+                if (mRegisterPackage) {
+                    try {
+                        ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
+                    } catch (RemoteException e) {
+                    }
+                }
+
                 zipPaths.add(mAppDir);
                 if (mSplitAppDirs != null) {
                     Collections.addAll(zipPaths, mSplitAppDirs);
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 8a53e08..ff2a174 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.os.Build;
 import android.os.Bundle;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
@@ -28,8 +29,6 @@
 
 import com.android.internal.R;
 
-import static android.os.Build.VERSION_CODES.L;
-
 /**
  * A dialog that prompts the user for the time of day using a {@link TimePicker}.
  *
@@ -102,26 +101,26 @@
             OnTimeSetListener callBack,
             int hourOfDay, int minute, boolean is24HourView) {
         super(context, resolveDialogTheme(context, theme));
+
         mTimeSetCallback = callBack;
         mInitialHourOfDay = hourOfDay;
         mInitialMinute = minute;
         mIs24HourView = is24HourView;
 
-        Context themeContext = getContext();
+        final Context themeContext = getContext();
 
-        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
-        if (targetSdkVersion < L) {
+        final int targetSdkVersion = themeContext.getApplicationInfo().targetSdkVersion;
+        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);
         }
 
-        LayoutInflater inflater =
-                (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        View view = inflater.inflate(R.layout.time_picker_dialog, null);
+        final LayoutInflater inflater = LayoutInflater.from(themeContext);
+        final View view = inflater.inflate(R.layout.time_picker_dialog, null);
         setView(view);
-        mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
 
+        mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
         mTimePicker.setShowDoneButton(true);
         mTimePicker.setDismissCallback(new TimePicker.TimePickerDismissCallback() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4e7dac0..71ad0c9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2270,6 +2270,45 @@
     }
 
     /**
+     * Called by a profile owner to set whether caller-Id information from the managed
+     * profile will be shown for incoming calls.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param who Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param disabled If true caller-Id information in the managed profile is not displayed.
+     */
+    public void setCrossProfileCallerIdDisabled(ComponentName who, boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setCrossProfileCallerIdDisabled(who, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Determine whether or not caller-Id information has been disabled.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param who Which {@link DeviceAdminReceiver} this request is associated with.
+     */
+    public boolean getCrossProfileCallerIdDisabled(ComponentName who) {
+        if (mService != null) {
+            try {
+                return mService.getCrossProfileCallerIdDisabled(who);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by the profile owner so that some intents sent in the managed profile can also be
      * resolved in the parent, or vice versa.
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -2598,9 +2637,17 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
+        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
+    }
+
+    /**
+     * @see #getAccountTypesWithManagementDisabled()
+     * @hide
+     */
+    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
         if (mService != null) {
             try {
-                return mService.getAccountTypesWithManagementDisabled();
+                return mService.getAccountTypesWithManagementDisabledAsUser(userId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d36497e..9b1979f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -146,6 +146,7 @@
 
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
+    String[] getAccountTypesWithManagementDisabledAsUser(int userId);
 
     void setLockTaskPackages(in String[] packages);
     String[] getLockTaskPackages();
@@ -161,4 +162,8 @@
 
     void setBlockUninstall(in ComponentName admin, in String packageName, boolean blockUninstall);
     boolean getBlockUninstall(in ComponentName admin, in String packageName);
+
+    void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
+    boolean getCrossProfileCallerIdDisabled(in ComponentName who);
+    boolean getCrossProfileCallerIdDisabledForUser(int userId);
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index e2a86e8..87d785a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -209,7 +209,7 @@
      *            output stream.
      */
     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) throws IOException;
+            ParcelFileDescriptor newState) throws IOException;
 
     /**
      * The application is being restored from backup and should replace any
@@ -243,8 +243,7 @@
      *            When a full-backup dataset is being restored, this will be <code>null</code>.
      */
     public abstract void onRestore(BackupDataInput data, int appVersionCode,
-            ParcelFileDescriptor newState)
-            throws IOException;
+            ParcelFileDescriptor newState) throws IOException;
 
     /**
      * The application is having its entire file system contents backed up.  {@code data}
@@ -575,6 +574,20 @@
         FullBackup.restoreFile(data, size, type, mode, mtime, null);
     }
 
+    /**
+     * The application's restore operation has completed.  This method is called after
+     * all available data has been delivered to the application for restore (via either
+     * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or
+     * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}
+     * callbacks).  This provides the app with a stable end-of-restore opportunity to
+     * perform any appropriate post-processing on the data that was just delivered.
+     *
+     * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor)
+     * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
+     */
+    public void onRestoreFinished() {
+    }
+
     // ----- Core implementation -----
 
     /** @hide */
@@ -723,6 +736,24 @@
         }
 
         @Override
+        public void doRestoreFinished(int token, IBackupManager callbackBinder) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                BackupAgent.this.onRestoreFinished();
+            } finally {
+                // Ensure that any side-effect SharedPreferences writes have landed
+                waitForSharedPrefs();
+
+                Binder.restoreCallingIdentity(ident);
+                try {
+                    callbackBinder.opComplete(token);
+                } catch (RemoteException e) {
+                    // we'll time out anyway, so we're safe
+                }
+            }
+        }
+
+        @Override
         public void fail(String message) {
             getHandler().post(new FailRunnable(message));
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7d15233..6c7197e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2475,11 +2475,11 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
-     * android.net.ethernet.EthernetManager} for handling management of
+     * android.net.EthernetManager} for handling management of
      * Ethernet access.
      *
      * @see #getSystemService
-     * @see android.net.ethernet.EthernetManager
+     * @see android.net.EthernetManager
      *
      * @hide
      */
@@ -2859,6 +2859,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.
      *
@@ -3251,6 +3260,12 @@
     public static final int CONTEXT_RESTRICTED = 0x00000004;
 
     /**
+     * @hide Used to indicate we should tell the activity manager about the process
+     * loading this code.
+     */
+    public static final int CONTEXT_REGISTER_PACKAGE = 0x40000000;
+
+    /**
      * Return a new Context object for the given application name.  This
      * Context is the same as what the named application gets when it is
      * launched, containing the same resources and class loader.  Each call to
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 37df29a..ee7810d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1033,6 +1033,45 @@
     public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL full hardware}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR manual sensor}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR =
+            "android.hardware.camera.capability.manual_sensor";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING manual post-processing}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING =
+            "android.hardware.camera.capability.manual_post_processing";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_RAW =
+            "android.hardware.camera.capability.raw";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * consumer IR devices.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab33d75..833dc21 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -836,10 +836,6 @@
             pkg.baseCodePath = apkPath;
             pkg.mSignatures = null;
 
-            // TODO: Remove this when the WebView can load resources dynamically. b/11505352
-            pkg.usesOptionalLibraries = ArrayUtils.add(pkg.usesOptionalLibraries,
-                    "com.android.webview");
-
             return pkg;
 
         } catch (PackageParserException e) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 3a30123..2684e6c 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -100,7 +100,7 @@
         synchronized (sSync) {
             if (sSystem == null) {
                 AssetManager system = new AssetManager(true);
-                system.makeStringBlocks(false);
+                system.makeStringBlocks(null);
                 sSystem = system;
             }
         }
@@ -246,21 +246,21 @@
         if (mStringBlocks == null) {
             synchronized (this) {
                 if (mStringBlocks == null) {
-                    makeStringBlocks(true);
+                    makeStringBlocks(sSystem.mStringBlocks);
                 }
             }
         }
     }
 
-    /*package*/ final void makeStringBlocks(boolean copyFromSystem) {
-        final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0;
+    /*package*/ final void makeStringBlocks(StringBlock[] seed) {
+        final int seedNum = (seed != null) ? seed.length : 0;
         final int num = getStringBlockCount();
         mStringBlocks = new StringBlock[num];
         if (localLOGV) Log.v(TAG, "Making string blocks for " + this
                 + ": " + num);
         for (int i=0; i<num; i++) {
-            if (i < sysNum) {
-                mStringBlocks[i] = sSystem.mStringBlocks[i];
+            if (i < seedNum) {
+                mStringBlocks[i] = seed[i];
             } else {
                 mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
             }
@@ -610,8 +610,11 @@
      * {@hide}
      */
     public final int addAssetPath(String path) {
-        int res = addAssetPathNative(path);
-        return res;
+        synchronized (this) {
+            int res = addAssetPathNative(path);
+            makeStringBlocks(mStringBlocks);
+            return res;
+        }
     }
 
     private native final int addAssetPathNative(String path);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4976a48..ff861209 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -887,7 +887,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
-     * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
      */
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
             new Key<int[]>("android.request.availableCapabilities", int[].class);
@@ -1068,17 +1068,17 @@
      * <tr>
      * <td align="left">RAW_OPAQUE</td>
      * <td align="left">RAW16</td>
-     * <td align="left">DNG</td>
+     * <td align="left">RAW</td>
      * </tr>
      * <tr>
      * <td align="left">RAW16</td>
      * <td align="left">YUV_420_888</td>
-     * <td align="left">DNG</td>
+     * <td align="left">RAW</td>
      * </tr>
      * <tr>
      * <td align="left">RAW16</td>
      * <td align="left">JPEG</td>
-     * <td align="left">DNG</td>
+     * <td align="left">RAW</td>
      * </tr>
      * </tbody>
      * </table>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 36b1089..63130a7 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -387,8 +387,11 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2;
 
     /**
-     * <p>The camera device supports outputting RAW buffers that can be
-     * saved offline into a DNG format.</p>
+     * <p>The camera device supports outputting RAW buffers and
+     * metadata for interpreting them.</p>
+     * <p>Devices supporting the RAW capability allow both for
+     * saving DNG files, and for direct application processing of
+     * raw sensor images.</p>
      * <ul>
      * <li>RAW_SENSOR is supported as an output format.</li>
      * <li>The maximum available resolution for RAW_SENSOR streams
@@ -403,7 +406,7 @@
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
-    public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 3;
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
 
     /**
      * <p>The camera device supports the Zero Shutter Lag use case.</p>
@@ -1186,8 +1189,7 @@
      * image while recording video) use case.</p>
      * <p>The camera device should take the highest-quality image
      * possible (given the other settings) without disrupting the
-     * frame rate of video recording.<br />
-     * </p>
+     * frame rate of video recording.  </p>
      * @see CaptureRequest#CONTROL_CAPTURE_INTENT
      */
     public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 92046f3..8750194 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -81,6 +81,7 @@
 
     private final String mCameraId;
     private final CameraCharacteristics mCharacteristics;
+    private final int mTotalPartialCount;
 
     /**
      * A list tracking request and its expected last frame.
@@ -210,7 +211,7 @@
 
     public CameraDeviceImpl(String cameraId, StateListener listener, Handler handler,
                         CameraCharacteristics characteristics) {
-        if (cameraId == null || listener == null || handler == null) {
+        if (cameraId == null || listener == null || handler == null || characteristics == null) {
             throw new IllegalArgumentException("Null argument given");
         }
         mCameraId = cameraId;
@@ -226,6 +227,15 @@
         }
         TAG = tag;
         DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+        Integer partialCount =
+                mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
+        if (partialCount == null) {
+            // 1 means partial result is not supported.
+            mTotalPartialCount = 1;
+        } else {
+            mTotalPartialCount = partialCount;
+        }
     }
 
     public CameraDeviceCallbacks getCallbacks() {
@@ -1042,11 +1052,11 @@
                 final CaptureListenerHolder holder =
                         CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
 
-                Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
-                boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+                boolean isPartialResult =
+                        (resultExtras.getPartialResultCount() < mTotalPartialCount);
 
                 // Update tracker (increment counter) when it's not a partial result.
-                if (!quirkIsPartialResult) {
+                if (!isPartialResult) {
                     mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(),
                             /*error*/false);
                 }
@@ -1076,7 +1086,7 @@
                 Runnable resultDispatch = null;
 
                 // Either send a partial result or the final capture completed result
-                if (quirkIsPartialResult) {
+                if (isPartialResult) {
                     final CaptureResult resultAsCapture =
                             new CaptureResult(result, request, requestId);
 
@@ -1113,7 +1123,7 @@
                 holder.getHandler().post(resultDispatch);
 
                 // Fire onCaptureSequenceCompleted
-                if (!quirkIsPartialResult) {
+                if (!isPartialResult) {
                     checkAndFireSequenceComplete();
                 }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ebab563..ec1fc4a 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -475,7 +475,7 @@
                     @Override
                     @SuppressWarnings("unchecked")
                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
-                        return (T) metadata.getFaceRectangles();
+                        return (T) metadata.getFaces();
                     }
                 });
         sGetCommandMap.put(
@@ -483,7 +483,7 @@
                     @Override
                     @SuppressWarnings("unchecked")
                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
-                        return (T) metadata.getFaces();
+                        return (T) metadata.getFaceRectangles();
                     }
                 });
         sGetCommandMap.put(
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index 7544045..d859da7 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -27,6 +27,7 @@
     private int afTriggerId;
     private int precaptureTriggerId;
     private long frameNumber;
+    private int partialResultCount;
 
     public static final Parcelable.Creator<CaptureResultExtras> CREATOR =
             new Parcelable.Creator<CaptureResultExtras>() {
@@ -46,12 +47,14 @@
     }
 
     public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
-                               int precaptureTriggerId, long frameNumber) {
+                               int precaptureTriggerId, long frameNumber,
+                               int partialResultCount) {
         this.requestId = requestId;
         this.subsequenceId = subsequenceId;
         this.afTriggerId = afTriggerId;
         this.precaptureTriggerId = precaptureTriggerId;
         this.frameNumber = frameNumber;
+        this.partialResultCount = partialResultCount;
     }
 
     @Override
@@ -66,6 +69,7 @@
         dest.writeInt(afTriggerId);
         dest.writeInt(precaptureTriggerId);
         dest.writeLong(frameNumber);
+        dest.writeInt(partialResultCount);
     }
 
     public void readFromParcel(Parcel in) {
@@ -74,6 +78,7 @@
         afTriggerId = in.readInt();
         precaptureTriggerId = in.readInt();
         frameNumber = in.readLong();
+        partialResultCount = in.readInt();
     }
 
     public int getRequestId() {
@@ -96,4 +101,7 @@
         return frameNumber;
     }
 
+    public int getPartialResultCount() {
+        return partialResultCount;
+    }
 }
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 5f29e5c..71d3d4b 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -73,10 +73,11 @@
     private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
         if (holder == null) {
             return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
-                    ILLEGAL_VALUE, ILLEGAL_VALUE);
+                    ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
         }
         return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
-                /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber());
+                /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
+                /*partialResultCount*/1);
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 79673b3..ce7a2a42 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/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index c74134a..2e96487 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -156,6 +156,9 @@
             if (searchKeyphraseId != -1) {
                 String searchKeyphrase = array.getString(com.android.internal.R.styleable
                         .VoiceEnrollmentApplication_searchKeyphrase);
+                if (searchKeyphrase == null) {
+                    searchKeyphrase = "";
+                }
                 String searchKeyphraseSupportedLocales =
                         array.getString(com.android.internal.R.styleable
                                 .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
@@ -165,9 +168,11 @@
                         && !searchKeyphraseSupportedLocales.isEmpty()) {
                     supportedLocales = searchKeyphraseSupportedLocales.split(",");
                 }
+                int recognitionModes = array.getInt(com.android.internal.R.styleable
+                        .VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, 0);
                 mKeyphrases = new KeyphraseMetadata[1];
                 mKeyphrases[0] = new KeyphraseMetadata(
-                        searchKeyphraseId, searchKeyphrase, supportedLocales);
+                        searchKeyphraseId, searchKeyphrase, supportedLocales, recognitionModes);
             } else {
                 mParseError = "searchKeyphraseId not specified in meta-data";
                 return;
@@ -239,8 +244,8 @@
      * @param keyphrase The keyphrase that the user needs to be enrolled to.
      * @param locale The locale for which the enrollment needs to be performed.
      *        This is a Java locale, for example "en_US".
-     * @return The metadata, if an enrollment client supports the given keyphrase
-     *         and the given locale, null otherwise.
+     * @return The metadata, if the enrollment client supports the given keyphrase
+     *         and locale, null otherwise.
      */
     public KeyphraseMetadata getKeyphraseMetadata(String keyphrase, String locale) {
         if (mKeyphrases == null || mKeyphrases.length == 0) {
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
index 03a4939..38305f9 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
@@ -27,34 +27,36 @@
     public final int id;
     public final String keyphrase;
     public final ArraySet<String> supportedLocales;
+    public final int recognitionModeFlags;
 
-    public KeyphraseMetadata(int id, String keyphrase, String[] supportedLocales) {
+    public KeyphraseMetadata(int id, String keyphrase, String[] supportedLocales,
+            int recognitionModeFlags) {
         this.id = id;
         this.keyphrase = keyphrase;
         this.supportedLocales = new ArraySet<String>(supportedLocales.length);
         for (String locale : supportedLocales) {
             this.supportedLocales.add(locale);
         }
+        this.recognitionModeFlags = recognitionModeFlags;
     }
 
     @Override
     public String toString() {
-        return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales;
+        return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales
+                + ", recognition-modes=" + recognitionModeFlags;
     }
 
     /**
      * @return Indicates if we support the given phrase.
      */
     public boolean supportsPhrase(String phrase) {
-        // TODO(sansid): Come up with a scheme for custom keyphrases that should always match.
-        return keyphrase.equalsIgnoreCase(phrase);
+        return keyphrase.isEmpty() || keyphrase.equalsIgnoreCase(phrase);
     }
 
     /**
      * @return Indicates if we support the given locale.
      */
     public boolean supportsLocale(String locale) {
-        // TODO(sansid): Come up with a scheme for keyphrases that are available in all locales.
-        return supportedLocales.contains(locale);
+        return supportedLocales.isEmpty() || supportedLocales.contains(locale);
     }
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerHelper.java b/core/java/android/hardware/soundtrigger/SoundTriggerHelper.java
index 3659621..431d550 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerHelper.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerHelper.java
@@ -65,8 +65,12 @@
      * The callback for sound trigger events.
      */
     public interface Listener {
-        /** Called when the given keyphrase is spoken. */
-        void onKeyphraseSpoken();
+        /**
+         * Called when the given keyphrase is spoken.
+         *
+         * @param data The captured audio, may be null.
+         */
+        void onKeyphraseSpoken(byte[] data);
 
         /**
          * Called when the listening state for the given keyphrase changes.
@@ -80,7 +84,7 @@
         int status = SoundTrigger.listModules(modules);
         mActiveListeners = new SparseArray<>(1);
         if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
-            // TODO: Figure out how to handle errors in listing the modules here.
+            Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
             dspInfo = null;
             mModuleProperties = null;
             mModule = null;
@@ -98,8 +102,11 @@
      * @return True, if a recognition for the given {@link Keyphrase} is active.
      */
     public synchronized boolean isKeyphraseActive(Keyphrase keyphrase) {
-        // TODO: Check if the recognition for the keyphrase is currently active.
-        return false;
+        if (keyphrase == null) {
+            Slog.w(TAG, "isKeyphraseActive requires a non-null keyphrase");
+            return false;
+        }
+        return mActiveListeners.get(keyphrase.id) != null;
     }
 
     /**
@@ -223,7 +230,8 @@
 
         switch (event.status) {
             case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
-                listener.onKeyphraseSpoken();
+                // TODO: Pass the captured audio back.
+                listener.onKeyphraseSpoken(null);
                 break;
             case SoundTrigger.RECOGNITION_STATUS_ABORT:
                 listener.onListeningStateChanged(STATE_STOPPED);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8caea25..afac239 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -131,6 +131,12 @@
     public static final int PACKAGE_INFO_GID = 1032;
 
     /**
+     * Defines the UID/GID for the shared RELRO file updater process.
+     * @hide
+     */
+    public static final int SHARED_RELRO_UID = 1037;
+
+    /**
      * Defines the start of a range of UIDs (and GIDs), going from this
      * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
      * to applications.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 707d31a..d3aee50 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -138,7 +138,8 @@
     public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
 
     /**
-     * Key for user restrictions. Specifies if a user is disallowed from removing users.
+     * Key for user restrictions. Specifies if a user is disallowed from removing itself and other
+     * users.
      * The default value is <code>false</code>.
      * <p/>
      * Type: Boolean
@@ -270,15 +271,26 @@
     public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
 
     /**
-     * Key for user restrictions. Specifies that the user is not allowed to send or receive
-     * phone calls or text messages. Emergency calls may still be permitted.
+     * Key for user restrictions. Specifies that the user is not allowed to make outgoing
+     * phone calls. Emergency calls are still permitted.
      * The default value is <code>false</code>.
      * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String DISALLOW_TELEPHONY = "no_telephony";
+    public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+
+    /**
+     * Key for user restrictions. Specifies that the user is not allowed to send or receive
+     * SMS messages.
+     * The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SMS = "no_sms";
 
     /**
      * Key for user restrictions. Specifies that windows besides app windows should not be
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/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 306543f..048fda1 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -66,18 +66,42 @@
     public static final int MANAGE_ACTION_UN_ENROLL = 2;
 
     /**
-     * Return codes for {@link #startRecognition()}, {@link #stopRecognition()}
+     * Return codes for {@link #startRecognition(int)}, {@link #stopRecognition()}
      */
     public static final int STATUS_ERROR = Integer.MIN_VALUE;
     public static final int STATUS_OK = 1;
 
     //---- Keyphrase recognition status ----//
-    // TODO: Figure out if they are exclusive or should be flags instead?
-    public static final int RECOGNITION_NOT_AVAILABLE = -3;
-    public static final int RECOGNITION_NOT_REQUESTED = -2;
-    public static final int RECOGNITION_DISABLED_TEMPORARILY = -1;
-    public static final int RECOGNITION_REQUESTED = 1;
-    public static final int RECOGNITION_ACTIVE = 2;
+    /** Indicates that recognition is not available. */
+    public static final int RECOGNITION_STATUS_NOT_AVAILABLE = 0x01;
+    /** Indicates that recognition has not been requested. */
+    public static final int RECOGNITION_STATUS_NOT_REQUESTED = 0x02;
+    /** Indicates that recognition has been requested. */
+    public static final int RECOGNITION_STATUS_REQUESTED = 0x04;
+    /** Indicates that recognition has been temporarily disabled. */
+    public static final int RECOGNITION_STATUS_DISABLED_TEMPORARILY = 0x08;
+    /** Indicates that recognition is currently active . */
+    public static final int RECOGNITION_STATUS_ACTIVE = 0x10;
+
+    //-- Flags for startRecogntion    ----//
+    /** Empty flag for {@link #startRecognition(int)}. */
+    public static final int RECOGNITION_FLAG_NONE = 0;
+    /**
+     * Recognition flag for {@link #startRecognition(int)} that indicates
+     * whether the trigger audio for hotword needs to be captured.
+     */
+    public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 0x1;
+
+    //---- Recognition mode flags ----//
+    // Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags.
+
+    /** Simple recognition of the key phrase. Returned by {@link #getRecognitionStatus()} */
+    public static final int RECOGNITION_MODE_VOICE_TRIGGER
+            = SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+    /** Trigger only if one user is identified. Returned by {@link #getRecognitionStatus()} */
+    public static final int RECOGNITION_MODE_USER_IDENTIFICATION
+            = SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+
     static final String TAG = "AlwaysOnHotwordDetector";
 
     private final String mText;
@@ -107,9 +131,11 @@
     public interface Callback {
         /**
          * Called when the keyphrase is spoken.
-         * TODO: Add more data to the callback.
+         *
+         * @param data Optional trigger audio data, if it was requested during
+         *        {@link AlwaysOnHotwordDetector#startRecognition(int)}.
          */
-        void onDetected();
+        void onDetected(byte[] data);
         /**
          * Called when the detection for the associated keyphrase starts.
          */
@@ -166,47 +192,63 @@
     }
 
     /**
-     * Gets the status of the recognition.
-     * @return One of {@link #RECOGNITION_NOT_AVAILABLE}, {@link #RECOGNITION_NOT_REQUESTED},
-     *         {@link #RECOGNITION_DISABLED_TEMPORARILY} or {@link #RECOGNITION_ACTIVE}.
-     * @throws UnsupportedOperationException if the recognition isn't supported.
+     * Gets the recognition modes supported by the associated keyphrase.
+     *
+     * @throws UnsupportedOperationException if the keyphrase itself isn't supported.
      *         Callers should check the availability by calling {@link #getAvailability()}
      *         before calling this method to avoid this exception.
      */
-    public int getRecognitionStatus() {
-        if (mAvailability != KEYPHRASE_ENROLLED) {
+    public int getSupportedRecognitionModes() {
+        if (mAvailability == KEYPHRASE_HARDWARE_UNAVAILABLE
+                || mAvailability == KEYPHRASE_UNSUPPORTED) {
             throw new UnsupportedOperationException(
-                    "Recognition for the given keyphrase is not supported");
+                    "Getting supported recognition modes for the keyphrase is not supported");
         }
 
+        return mKeyphraseMetadata.recognitionModeFlags;
+    }
+
+    /**
+     * Gets the status of the recognition.
+     * @return A flag comprised of {@link #RECOGNITION_STATUS_NOT_AVAILABLE},
+     *         {@link #RECOGNITION_STATUS_NOT_REQUESTED}, {@link #RECOGNITION_STATUS_REQUESTED},
+     *         {@link #RECOGNITION_STATUS_DISABLED_TEMPORARILY} and
+     *         {@link #RECOGNITION_STATUS_ACTIVE}.
+     */
+    public int getRecognitionStatus() {
         return mRecognitionState;
     }
 
     /**
      * Starts recognition for the associated keyphrase.
      *
+     * @param recognitionFlags The flags to control the recognition properties.
+     *        The allowed flags are {@link #RECOGNITION_FLAG_NONE} and
+     *        {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO}.
      * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
      * @throws UnsupportedOperationException if the recognition isn't supported.
      *         Callers should check the availability by calling {@link #getAvailability()}
      *         before calling this method to avoid this exception.
      */
-    public int startRecognition() {
-        if (mAvailability != KEYPHRASE_ENROLLED) {
+    public int startRecognition(int recognitionFlags) {
+        if (mAvailability != KEYPHRASE_ENROLLED
+                || (mRecognitionState&RECOGNITION_STATUS_NOT_AVAILABLE) != 0) {
             throw new UnsupportedOperationException(
                     "Recognition for the given keyphrase is not supported");
         }
 
-        mRecognitionState = RECOGNITION_REQUESTED;
-        mRecognitionState = RECOGNITION_REQUESTED;
+        mRecognitionState &= RECOGNITION_STATUS_REQUESTED;
         KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1];
         // TODO: Do we need to do something about the confidence level here?
-        // TODO: Read the recognition mode flag from the KeyphraseMetadata.
         // TODO: Take in captureTriggerAudio as a method param here.
         recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id,
-                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, new ConfidenceLevel[0]);
+                mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]);
+        boolean captureTriggerAudio =
+                (recognitionFlags & RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
         int code = mSoundTriggerHelper.startRecognition(mKeyphraseMetadata.id,
                 mEnrolledSoundModel.convertToSoundTriggerKeyphraseSoundModel(), mListener,
-                new RecognitionConfig(false, recognitionExtra, null /* additional data */));
+                new RecognitionConfig(
+                        captureTriggerAudio, recognitionExtra,null /* additional data */));
         if (code != SoundTriggerHelper.STATUS_OK) {
             Slog.w(TAG, "startRecognition() failed with error code " + code);
             return STATUS_ERROR;
@@ -229,7 +271,7 @@
                     "Recognition for the given keyphrase is not supported");
         }
 
-        mRecognitionState = RECOGNITION_NOT_REQUESTED;
+        mRecognitionState &= ~RECOGNITION_STATUS_NOT_REQUESTED;
         int code = mSoundTriggerHelper.stopRecognition(mKeyphraseMetadata.id, mListener);
 
         if (code != SoundTriggerHelper.STATUS_OK) {
@@ -269,16 +311,21 @@
     private int internalGetAvailability() {
         // No DSP available
         if (mSoundTriggerHelper.dspInfo == null) {
+            mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
             return KEYPHRASE_HARDWARE_UNAVAILABLE;
         }
         // No enrollment application supports this keyphrase/locale
         if (mKeyphraseMetadata == null) {
+            mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
             return KEYPHRASE_UNSUPPORTED;
         }
         // This keyphrase hasn't been enrolled.
         if (mEnrolledSoundModel == null) {
+            mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
             return KEYPHRASE_UNENROLLED;
         }
+        // Mark recognition as available
+        mRecognitionState &= ~RECOGNITION_STATUS_NOT_AVAILABLE;
         return KEYPHRASE_ENROLLED;
     }
 
@@ -320,14 +367,15 @@
         }
 
         @Override
-        public void onKeyphraseSpoken() {
+        public void onKeyphraseSpoken(byte[] data) {
             Slog.i(TAG, "onKeyphraseSpoken");
-            mCallback.onDetected();
+            mCallback.onDetected(data);
         }
 
         @Override
         public void onListeningStateChanged(int state) {
             Slog.i(TAG, "onListeningStateChanged: state=" + state);
+            // TODO: Set/unset the RECOGNITION_STATUS_ACTIVE flag here.
             if (state == SoundTriggerHelper.STATE_STARTED) {
                 mCallback.onDetectionStarted();
             } else if (state == SoundTriggerHelper.STATE_STOPPED) {
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e9e2f4c..c9915a2 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -20,4 +20,5 @@
  * @hide
  */
 oneway interface IVoiceInteractionService {
+    void ready();
 }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index a9b1959..1f5d327 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -18,15 +18,19 @@
 
 import android.annotation.SdkConstant;
 import android.app.Service;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
 import android.hardware.soundtrigger.SoundTriggerHelper;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
+import android.provider.Settings;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractionManagerService;
 
@@ -64,14 +68,58 @@
     public static final String SERVICE_META_DATA = "android.voice_interaction";
 
     IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
+        @Override public void ready() {
+            mHandler.sendEmptyMessage(MSG_READY);
+        }
     };
 
+    MyHandler mHandler;
+
     IVoiceInteractionManagerService mSystemService;
 
     private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
     private SoundTriggerHelper mSoundTriggerHelper;
 
+    static final int MSG_READY = 1;
+
+    class MyHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_READY:
+                    onReady();
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    /**
+     * Check whether the given service component is the currently active
+     * VoiceInteractionService.
+     */
+    public static boolean isActiveService(Context context, ComponentName service) {
+        String cur = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.VOICE_INTERACTION_SERVICE);
+        if (cur == null || cur.isEmpty()) {
+            return false;
+        }
+        ComponentName curComp = ComponentName.unflattenFromString(cur);
+        if (curComp == null) {
+            return false;
+        }
+        return curComp.equals(cur);
+    }
+
+    /**
+     * Initiate the execution of a new {@link android.service.voice.VoiceInteractionSession}.
+     * @param args Arbitrary arguments that will be propagated to the session.
+     */
     public void startSession(Bundle args) {
+        if (mSystemService == null) {
+            throw new IllegalStateException("Not available until onReady() is called");
+        }
         try {
             mSystemService.startSession(mInterface, args);
         } catch (RemoteException e) {
@@ -81,10 +129,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
-                ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
-        mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
-        mSoundTriggerHelper = new SoundTriggerHelper();
+        mHandler = new MyHandler();
     }
 
     @Override
@@ -96,6 +141,19 @@
     }
 
     /**
+     * Called during service initialization to tell you when the system is ready
+     * to receive interaction from it.  You should generally do initialization here
+     * rather than in {@link #onCreate()}.  Methods such as {@link #startSession}
+     * and {@link #getAlwaysOnHotwordDetector} will not be operational until this point.
+     */
+    public void onReady() {
+        mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
+                ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+        mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
+        mSoundTriggerHelper = new SoundTriggerHelper();
+    }
+
+    /**
      * @param keyphrase The keyphrase that's being used, for example "Hello Android".
      * @param locale The locale for which the enrollment needs to be performed.
      *        This is a Java locale, for example "en_US".
@@ -104,6 +162,11 @@
      */
     public final AlwaysOnHotwordDetector getAlwaysOnHotwordDetector(
             String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
+        if (mSystemService == null) {
+            throw new IllegalStateException("Not available until onReady() is called");
+        }
+        // TODO: Cache instances and return the same one instead of creating a new interactor
+        // for the same keyphrase/locale combination.
         return new AlwaysOnHotwordDetector(keyphrase, locale, callback,
                 mKeyphraseEnrollmentInfo, mSoundTriggerHelper, mInterface, mSystemService);
     }
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index a909ead..d27e2cd 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -40,6 +40,7 @@
 
     private ServiceInfo mServiceInfo;
     private String mSessionService;
+    private String mRecognitionService;
     private String mSettingsActivity;
 
     public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp)
@@ -82,6 +83,8 @@
                     com.android.internal.R.styleable.VoiceInteractionService);
             mSessionService = array.getString(
                     com.android.internal.R.styleable.VoiceInteractionService_sessionService);
+            mRecognitionService = array.getString(
+                    com.android.internal.R.styleable.VoiceInteractionService_recognitionService);
             mSettingsActivity = array.getString(
                     com.android.internal.R.styleable.VoiceInteractionService_settingsActivity);
             array.recycle();
@@ -119,6 +122,10 @@
         return mSessionService;
     }
 
+    public String getRecognitionService() {
+        return mRecognitionService;
+    }
+
     public String getSettingsActivity() {
         return mSettingsActivity;
     }
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
index c13b47f..b4ac429 100644
--- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
@@ -54,7 +54,11 @@
         final UtteranceProgressDispatcher dispatcher = getDispatcher();
 
         dispatcher.dispatchOnStart();
-        mPlayer = MediaPlayer.create(mContext, mUri);
+
+        int sessionId = mAudioParams.mSessionId;
+        mPlayer = MediaPlayer.create(
+                mContext, mUri, null, mAudioParams.mAudioAttributes,
+                sessionId > 0 ? sessionId : AudioSystem.AUDIO_SESSION_ALLOCATE);
         if (mPlayer == null) {
             dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
             return;
@@ -76,11 +80,8 @@
                     mDone.open();
                 }
             });
-            mPlayer.setAudioStreamType(mAudioParams.mStreamType);
+
             setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
-            if (mAudioParams.mSessionId != AudioSystem.AUDIO_SESSION_ALLOCATE) {
-                mPlayer.setAudioSessionId(mAudioParams.mSessionId);
-            }
             mPlayer.start();
             mDone.block();
             finish();
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index b405de0..dc4e9d3 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -2,6 +2,7 @@
 
 package android.speech.tts;
 
+import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioTrack;
 import android.speech.tts.TextToSpeechService.AudioOutputParams;
@@ -214,9 +215,14 @@
                 = AudioTrack.getMinBufferSize(mSampleRateInHz, channelConfig, mAudioFormat);
         int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
 
-        AudioTrack audioTrack = new AudioTrack(mAudioParams.mStreamType, mSampleRateInHz,
-                channelConfig, mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
+        AudioFormat audioFormat = (new AudioFormat.Builder())
+                .setChannelMask(channelConfig)
+                .setEncoding(mAudioFormat)
+                .setSampleRate(mSampleRateInHz).build();
+        AudioTrack audioTrack = new AudioTrack(mAudioParams.mAudioAttributes,
+                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
                 mAudioParams.mSessionId);
+
         if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
             Log.w(TAG, "Unable to create audio track.");
             audioTrack.release();
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 46077ed..d8b9b5f 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -524,6 +525,17 @@
         public static final String KEY_PARAM_STREAM = "streamType";
 
         /**
+         * Parameter key to specify the audio attributes to be used when
+         * speaking text or playing back a file. The value should be set
+         * using {@link TextToSpeech#setAudioAttributes(AudioAttributes)}.
+         *
+         * @see TextToSpeech#speak(String, int, HashMap)
+         * @see TextToSpeech#playEarcon(String, int, HashMap)
+         * @hide
+         */
+        public static final String KEY_PARAM_AUDIO_ATTRIBUTES = "audioAttributes";
+
+        /**
          * Parameter key to identify an utterance in the
          * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
          * spoken, a file has been played back or a silence duration has elapsed.
@@ -1357,6 +1369,25 @@
     }
 
     /**
+     * Sets the audio attributes to be used when speaking text or playing
+     * back a file.
+     *
+     * @param audioAttributes Valid AudioAttributes instance.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS}.
+     */
+    public int setAudioAttributes(AudioAttributes audioAttributes) {
+        if (audioAttributes != null) {
+            synchronized (mStartLock) {
+                mParams.putParcelable(Engine.KEY_PARAM_AUDIO_ATTRIBUTES,
+                    audioAttributes);
+            }
+            return SUCCESS;
+        }
+        return ERROR;
+    }
+
+    /**
      * @return the engine currently in use by this TextToSpeech instance.
      * @hide
      */
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a0743f7..4fea109 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -17,7 +17,7 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.media.AudioManager;
+import android.media.AudioAttributes;
 import android.media.AudioSystem;
 import android.net.Uri;
 import android.os.Binder;
@@ -608,12 +608,6 @@
         public final int mSessionId;
 
         /**
-         * Audio stream type. Must be one of the STREAM_ contants defined in
-         * {@link android.media.AudioManager}.
-         */
-        public final int mStreamType;
-
-        /**
          * Volume, in the range [0.0f, 1.0f]. The default value is
          * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
          */
@@ -625,42 +619,62 @@
          */
         public final float mPan;
 
+
+        /**
+         * Audio attributes, set by {@link TextToSpeech#setAudioAttributes}
+         * or created from the value of {@link TextToSpeech.Engine#KEY_PARAM_STREAM}.
+         */
+        public final AudioAttributes mAudioAttributes;
+
         /** Create AudioOutputParams with default values */
         AudioOutputParams() {
             mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
-            mStreamType = Engine.DEFAULT_STREAM;
             mVolume = Engine.DEFAULT_VOLUME;
             mPan = Engine.DEFAULT_PAN;
+            mAudioAttributes = null;
         }
 
-        AudioOutputParams(int sessionId, int streamType, float volume, float pan) {
+        AudioOutputParams(int sessionId, float volume, float pan,
+                AudioAttributes audioAttributes) {
             mSessionId = sessionId;
-            mStreamType = streamType;
             mVolume = volume;
             mPan = pan;
+            mAudioAttributes = audioAttributes;
         }
 
         /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
-        static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle) {
+        static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle,
+                boolean isSpeech) {
             if (paramsBundle == null) {
                 return new AudioOutputParams();
             }
 
+            AudioAttributes audioAttributes =
+                    (AudioAttributes) paramsBundle.getParcelable(
+                            Engine.KEY_PARAM_AUDIO_ATTRIBUTES);
+            if (audioAttributes == null) {
+                int streamType = paramsBundle.getInt(
+                        Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+                audioAttributes = (new AudioAttributes.Builder())
+                        .setLegacyStreamType(streamType)
+                        .setContentType((isSpeech ?
+                                AudioAttributes.CONTENT_TYPE_SPEECH :
+                                AudioAttributes.CONTENT_TYPE_SONIFICATION))
+                        .build();
+            }
+
             return new AudioOutputParams(
                     paramsBundle.getInt(
                             Engine.KEY_PARAM_SESSION_ID,
                             AudioSystem.AUDIO_SESSION_ALLOCATE),
-                    paramsBundle.getInt(
-                            Engine.KEY_PARAM_STREAM,
-                            Engine.DEFAULT_STREAM),
                     paramsBundle.getFloat(
                             Engine.KEY_PARAM_VOLUME,
                             Engine.DEFAULT_VOLUME),
                     paramsBundle.getFloat(
                             Engine.KEY_PARAM_PAN,
-                            Engine.DEFAULT_PAN));
+                            Engine.DEFAULT_PAN),
+                    audioAttributes);
         }
-
     }
 
 
@@ -832,7 +846,7 @@
         }
 
         AudioOutputParams getAudioParams() {
-            return AudioOutputParams.createFromV1ParamsBundle(mParams);
+            return AudioOutputParams.createFromV1ParamsBundle(mParams, true);
         }
     }
 
@@ -1005,6 +1019,11 @@
         public String getUtteranceId() {
             return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null);
         }
+
+        @Override
+        AudioOutputParams getAudioParams() {
+            return AudioOutputParams.createFromV1ParamsBundle(mParams, false);
+        }
     }
 
     private class SilenceSpeechItem extends UtteranceSpeechItem {
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index 04b2da5..3ab3b31 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -22,10 +22,13 @@
 import android.text.TextUtils;
 
 /**
- * A span that supplies additional meta-data intended for text-to-speech rendering
- * of the associated text.  If the text is being processed by a text-to-speech
- * engine, the engine may use the data in this span in addition to or instead of
- * its associated text.
+ * A span that supplies additional meta-data for the associated text intended
+ * for text-to-speech engines.  If the text is being processed by a
+ * text-to-speech engine, the engine may use the data in this span in addition
+ * to or instead of its associated text.
+ *
+ * The inner classes are there for convenience and provide builders for each
+ * TtsSpan type.
  */
 public class TtsSpan implements ParcelableSpan {
     private final String mType;
@@ -432,10 +435,18 @@
         mArgs = src.readPersistableBundle();
     }
 
+    /**
+     * Returns the type.
+     * @return The type of this instance.
+     */
     public String getType() {
         return mType;
     }
 
+    /**
+     * Returns a bundle of the arguments set.
+     * @return The bundle of the arguments set.
+     */
     public PersistableBundle getArgs() {
         return mArgs;
     }
@@ -455,4 +466,217 @@
     public int getSpanTypeId() {
         return TextUtils.TTS_SPAN;
     }
+
+    /**
+     * A simple builder for TtsSpans.
+     * This builder can be used directly, but the more specific subclasses of
+     * 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).
+     */
+    public static abstract class Builder<C extends Builder<C>> {
+        // Holds the type of this class.
+        private final String mType;
+
+        // Holds the arguments of this class. It only stores objects of type
+        // String, Integer and Long.
+        private PersistableBundle mArgs = new PersistableBundle();
+
+        public Builder(String type) {
+            mType = type;
+        }
+
+        /**
+         * Returns a TtsSpan built from the parameters set by the setter
+         * methods.
+         * @return A TtsSpan built with parameters of this builder.
+         */
+        public TtsSpan build() {
+            return new TtsSpan(mType, mArgs);
+        }
+
+        /**
+         * Sets an argument to a string value.
+         * @param arg The argument name.
+         * @param value The value the argument should be set to.
+         * @return This instance.
+         */
+        @SuppressWarnings("unchecked")
+        public C setStringArgument(String arg, String value) {
+            mArgs.putString(arg, value);
+            return (C) this;
+        }
+
+        /**
+         * Sets an argument to an int value.
+         * @param arg The argument name.
+         * @param value The value the argument should be set to.
+         */
+        @SuppressWarnings("unchecked")
+        public C setIntArgument(String arg, int value) {
+            mArgs.putInt(arg, value);
+            return (C) this;
+        }
+
+        /**
+         * Sets an argument to a long value.
+         * @param arg The argument name.
+         * @param value The value the argument should be set to.
+         */
+        @SuppressWarnings("unchecked")
+        public C setLongArgument(String arg, long value) {
+            mArgs.putLong(arg, value);
+            return (C) this;
+        }
+    }
+
+    /**
+     * A builder for TtsSpans, has setters for morphosyntactic features.
+     * This builder can be used directly, but the more specific subclasses of
+     * this builder like {@link TtsSpan.TextBuilder} and
+     * {@link TtsSpan.CardinalBuilder} are likely more useful.
+     */
+    public static class SemioticClassBuilder<C extends SemioticClassBuilder<C>>
+            extends Builder<C> {
+
+        public SemioticClassBuilder(String type) {
+            super(type);
+        }
+
+        /**
+         * Sets the gender information for this instance.
+         * @param gender Can any of {@link TtsSpan#GENDER_NEUTRAL},
+         *     {@link TtsSpan#GENDER_MALE} and {@link TtsSpan#GENDER_FEMALE}.
+         * @return This instance.
+         */
+        public C setGender(String gender) {
+            return setStringArgument(TtsSpan.ARG_GENDER, gender);
+        }
+
+        /**
+         * Sets the animacy information for this instance.
+         * @param animacy Can be any of {@link TtsSpan#ANIMACY_ANIMATE} and
+         *     {@link TtsSpan#ANIMACY_INANIMATE}.
+         * @return This instance.
+         */
+        public C setAnimacy(String animacy) {
+            return setStringArgument(TtsSpan.ARG_ANIMACY, animacy);
+        }
+
+        /**
+         * Sets the multiplicity information for this instance.
+         * @param multiplicity Can be any of
+         *     {@link TtsSpan#MULTIPLICITY_SINGLE},
+         *     {@link TtsSpan#MULTIPLICITY_DUAL} and
+         *     {@link TtsSpan#MULTIPLICITY_PLURAL}.
+         * @return This instance.
+         */
+        public C setMultiplicity(String multiplicity) {
+            return setStringArgument(TtsSpan.ARG_MULTIPLICITY, multiplicity);
+        }
+
+        /**
+         * Sets the grammatical case information for this instance.
+         * @param grammaticalCase Can be any of {@link TtsSpan#CASE_NOMINATIVE},
+         *     {@link TtsSpan#CASE_ACCUSATIVE}, {@link TtsSpan#CASE_DATIVE},
+         *     {@link TtsSpan#CASE_ABLATIVE}, {@link TtsSpan#CASE_GENITIVE},
+         *     {@link TtsSpan#CASE_VOCATIVE}, {@link TtsSpan#CASE_LOCATIVE} and
+         *     {@link TtsSpan#CASE_INSTRUMENTAL}.
+         * @return This instance.
+         */
+        public C setCase(String grammaticalCase) {
+            return setStringArgument(TtsSpan.ARG_CASE, grammaticalCase);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_TEXT}.
+     */
+    public static class TextBuilder extends SemioticClassBuilder<TextBuilder> {
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_TEXT}.
+         */
+        public TextBuilder() {
+            super(TtsSpan.TYPE_TEXT);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_TEXT} and sets the
+         * {@link TtsSpan#ARG_TEXT} argument.
+         * @param text The text to be synthesized.
+         * @see #setText(String)
+         */
+        public TextBuilder(String text) {
+            this();
+            setText(text);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_TEXT} argument, the text to be
+         * synthesized.
+         * @param text The string that will be synthesized.
+         * @return This instance.
+         */
+        public TextBuilder setText(String text) {
+            return setStringArgument(TtsSpan.ARG_TEXT, text);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_CARDINAL}.
+     */
+    public static class CardinalBuilder extends SemioticClassBuilder<CardinalBuilder> {
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL}.
+         */
+        public CardinalBuilder() {
+            super(TtsSpan.TYPE_CARDINAL);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL} and sets the
+         * {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number The number to synthesize.
+         * @see #setNumber(long)
+         */
+        public CardinalBuilder(long number) {
+            this();
+            setNumber(number);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL} and sets the
+         * {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number The number to synthesize.
+         * @see #setNumber(String)
+         */
+        public CardinalBuilder(String number) {
+            this();
+            setNumber(number);
+        }
+
+        /**
+         * Convenience method that converts the number to a String and set it to
+         * the value for {@link TtsSpan#ARG_NUMBER}.
+         * @param number The number that will be synthesized.
+         * @return This instance.
+         */
+        public CardinalBuilder 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 CardinalBuilder setNumber(String number) {
+            return setStringArgument(TtsSpan.ARG_NUMBER, number);
+        }
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a3e2c96..7bc8bc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18445,6 +18445,43 @@
     }
 
     /**
+     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+     *
+     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+     * before the child view consumes it. If this method returns <code>true</code>, a nested
+     * parent view consumed the fling and this view should not scroll as a result.</p>
+     *
+     * <p>For a better user experience, only one view in a nested scrolling chain should consume
+     * the fling at a time. If a parent view consumed the fling this method will return false.
+     * Custom view implementations should account for this in two ways:</p>
+     *
+     * <ul>
+     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+     *     position regardless.</li>
+     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
+     *     even to settle back to a valid idle position.</li>
+     * </ul>
+     *
+     * <p>Views should also not offer fling velocities to nested parent views along an axis
+     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+     * should not offer a horizontal fling velocity to its parents since scrolling along that
+     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @return true if a nested scrolling parent consumed the fling
+     */
+    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            return mNestedScrollingParent.onNestedPreFling(this, velocityX, velocityY);
+        }
+        return false;
+    }
+
+    /**
      * Gets a scale factor that determines the distance the view should scroll
      * vertically in response to {@link MotionEvent#ACTION_SCROLL}.
      * @return The vertical scroll scale factor.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ac70066..04c8b0b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6066,6 +6066,14 @@
     }
 
     /**
+     * @inheritDoc
+     */
+    @Override
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        return false;
+    }
+
+    /**
      * Return the current axes of nested scrolling for this ViewGroup.
      *
      * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 588b9cd..87a37f4 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -523,10 +523,32 @@
      * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
      *
      * @param target View that initiated the nested scroll
-     * @param velocityX Horizontal velocity in pixels per second.
+     * @param velocityX Horizontal velocity in pixels per second
      * @param velocityY Vertical velocity in pixels per second
      * @param consumed true if the child consumed the fling, false otherwise
      * @return true if this parent consumed or otherwise reacted to the fling
      */
     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+
+    /**
+     * React to a nested fling before the target view consumes it.
+     *
+     * <p>This method siginfies that a nested scrolling child has detected a fling with the given
+     * velocity along each axis. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling parent is consuming motion as part of a
+     * {@link #onNestedPreScroll(View, int, int, int[]) pre-scroll}, it may be appropriate for
+     * it to also consume the pre-fling to complete that same motion. By returning
+     * <code>true</code> from this method, the parent indicates that the child should not
+     * fling its own internal content as well.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param velocityX Horizontal velocity in pixels per second
+     * @param velocityY Vertical velocity in pixels per second
+     * @return true if this parent consumed the fling ahead of the target view
+     */
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8d3f99..5cad7db 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1052,25 +1052,24 @@
     }
 
     private boolean collectViewAttributes() {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        if (attachInfo.mRecomputeGlobalAttributes) {
+        if (mAttachInfo.mRecomputeGlobalAttributes) {
             //Log.i(TAG, "Computing view hierarchy attributes!");
-            attachInfo.mRecomputeGlobalAttributes = false;
-            boolean oldScreenOn = attachInfo.mKeepScreenOn;
-            attachInfo.mKeepScreenOn = false;
-            attachInfo.mSystemUiVisibility = 0;
-            attachInfo.mHasSystemUiListeners = false;
-            mView.dispatchCollectViewAttributes(attachInfo, 0);
-            attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility;
+            mAttachInfo.mRecomputeGlobalAttributes = false;
+            boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
+            mAttachInfo.mKeepScreenOn = false;
+            mAttachInfo.mSystemUiVisibility = 0;
+            mAttachInfo.mHasSystemUiListeners = false;
+            mView.dispatchCollectViewAttributes(mAttachInfo, 0);
+            mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
             WindowManager.LayoutParams params = mWindowAttributes;
-            attachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
-            if (attachInfo.mKeepScreenOn != oldScreenOn
-                    || attachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
-                    || attachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
+            mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
+            if (mAttachInfo.mKeepScreenOn != oldScreenOn
+                    || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
+                    || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
                 applyKeepScreenOnFlag(params);
-                params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
-                params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
-                mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility);
+                params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
+                params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
+                mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
                 return true;
             }
         }
@@ -1162,8 +1161,7 @@
      * @param m input matrix to modify
      */
     void transformMatrixToGlobal(Matrix m) {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        m.postTranslate(attachInfo.mWindowLeft, attachInfo.mWindowTop);
+        m.postTranslate(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
     }
 
     /**
@@ -1173,8 +1171,7 @@
      * @param m input matrix to modify
      */
     void transformMatrixToLocal(Matrix m) {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        m.preTranslate(-attachInfo.mWindowLeft, -attachInfo.mWindowTop);
+        m.preTranslate(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
     }
 
     void dispatchApplyInsets(View host) {
@@ -1216,8 +1213,6 @@
         int desiredWindowWidth;
         int desiredWindowHeight;
 
-        final View.AttachInfo attachInfo = mAttachInfo;
-
         final int viewVisibility = getHostVisibility();
         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                 || mNewSurfaceNeeded;
@@ -1266,10 +1261,10 @@
             // We used to use the following condition to choose 32 bits drawing caches:
             // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
             // However, windows are now always 32 bits by default, so choose 32 bits
-            attachInfo.mUse32BitDrawingCache = true;
-            attachInfo.mHasWindowFocus = false;
-            attachInfo.mWindowVisibility = viewVisibility;
-            attachInfo.mRecomputeGlobalAttributes = false;
+            mAttachInfo.mUse32BitDrawingCache = true;
+            mAttachInfo.mHasWindowFocus = false;
+            mAttachInfo.mWindowVisibility = viewVisibility;
+            mAttachInfo.mRecomputeGlobalAttributes = false;
             viewVisibilityChanged = false;
             mLastConfiguration.setTo(host.getResources().getConfiguration());
             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
@@ -1277,8 +1272,8 @@
             if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
                 host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
             }
-            host.dispatchAttachedToWindow(attachInfo, 0);
-            attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
+            host.dispatchAttachedToWindow(mAttachInfo, 0);
+            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
             dispatchApplyInsets(host);
             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
 
@@ -1295,7 +1290,7 @@
         }
 
         if (viewVisibilityChanged) {
-            attachInfo.mWindowVisibility = viewVisibility;
+            mAttachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                 destroyHardwareResources();
@@ -1308,7 +1303,7 @@
         }
 
         // Execute enqueued actions on every traversal in case a detached view enqueued an action
-        getRunQueue().executeActions(attachInfo.mHandler);
+        getRunQueue().executeActions(mAttachInfo.mHandler);
 
         boolean insetsChanged = false;
 
@@ -1364,21 +1359,21 @@
         if (collectViewAttributes()) {
             params = lp;
         }
-        if (attachInfo.mForceReportNewAttributes) {
-            attachInfo.mForceReportNewAttributes = false;
+        if (mAttachInfo.mForceReportNewAttributes) {
+            mAttachInfo.mForceReportNewAttributes = false;
             params = lp;
         }
 
-        if (mFirst || attachInfo.mViewVisibilityChanged) {
-            attachInfo.mViewVisibilityChanged = false;
+        if (mFirst || mAttachInfo.mViewVisibilityChanged) {
+            mAttachInfo.mViewVisibilityChanged = false;
             int resizeMode = mSoftInputMode &
                     WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
             // If we are in auto resize mode, then we need to determine
             // what mode to use now.
             if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
-                final int N = attachInfo.mScrollContainers.size();
+                final int N = mAttachInfo.mScrollContainers.size();
                 for (int i=0; i<N; i++) {
-                    if (attachInfo.mScrollContainers.get(i).isShown()) {
+                    if (mAttachInfo.mScrollContainers.get(i).isShown()) {
                         resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
                     }
                 }
@@ -1437,8 +1432,8 @@
         // If there are no inset listeners remaining then we may still need to compute
         // insets in case the old insets were non-empty and must be reset.
         final boolean computesInternalInsets =
-                attachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
-                || attachInfo.mHasNonEmptyGivenInternalInsets;
+                mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
+                || mAttachInfo.mHasNonEmptyGivenInternalInsets;
 
         boolean insetsPending = false;
         int relayoutResult = 0;
@@ -1659,8 +1654,8 @@
             if (DEBUG_ORIENTATION) Log.v(
                     TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
 
-            attachInfo.mWindowLeft = frame.left;
-            attachInfo.mWindowTop = frame.top;
+            mAttachInfo.mWindowLeft = frame.left;
+            mAttachInfo.mWindowTop = frame.top;
 
             // !!FIXME!! This next section handles the case where we did not get the
             // window size we asked for. We should avoid this by getting a maximum size from
@@ -1799,20 +1794,20 @@
             // true since we are comparing a not translated value to a translated one.
             // This scenario is rare but we may want to fix that.
 
-            final boolean windowMoved = (attachInfo.mWindowLeft != frame.left
-                    || attachInfo.mWindowTop != frame.top);
+            final boolean windowMoved = (mAttachInfo.mWindowLeft != frame.left
+                    || mAttachInfo.mWindowTop != frame.top);
             if (windowMoved) {
                 if (mTranslator != null) {
                     mTranslator.translateRectInScreenToAppWinFrame(frame);
                 }
-                attachInfo.mWindowLeft = frame.left;
-                attachInfo.mWindowTop = frame.top;
+                mAttachInfo.mWindowLeft = frame.left;
+                mAttachInfo.mWindowTop = frame.top;
             }
         }
 
         final boolean didLayout = layoutRequested && !mStopped;
         boolean triggerGlobalLayoutListener = didLayout
-                || attachInfo.mRecomputeGlobalAttributes;
+                || mAttachInfo.mRecomputeGlobalAttributes;
         if (didLayout) {
             performLayout(lp, desiredWindowWidth, desiredWindowHeight);
 
@@ -1851,18 +1846,18 @@
         }
 
         if (triggerGlobalLayoutListener) {
-            attachInfo.mRecomputeGlobalAttributes = false;
-            attachInfo.mTreeObserver.dispatchOnGlobalLayout();
+            mAttachInfo.mRecomputeGlobalAttributes = false;
+            mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
         }
 
         if (computesInternalInsets) {
             // Clear the original insets.
-            final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
+            final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
             insets.reset();
 
             // Compute new insets in place.
-            attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
-            attachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
+            mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
+            mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
 
             // Tell the window manager.
             if (insetsPending || !mLastGivenInsets.equals(insets)) {
@@ -1941,7 +1936,7 @@
             mReportNextDraw = true;
         }
 
-        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
+        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
                 viewVisibility != View.VISIBLE;
 
         if (!cancelDraw && !newSurface) {
@@ -2379,10 +2374,9 @@
 
         scrollToRectOrFocus(null, false);
 
-        final AttachInfo attachInfo = mAttachInfo;
-        if (attachInfo.mViewScrollChanged) {
-            attachInfo.mViewScrollChanged = false;
-            attachInfo.mTreeObserver.dispatchOnScrollChanged();
+        if (mAttachInfo.mViewScrollChanged) {
+            mAttachInfo.mViewScrollChanged = false;
+            mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
         }
 
         boolean animating = mScroller != null && mScroller.computeScrollOffset();
@@ -2397,8 +2391,8 @@
             fullRedrawNeeded = true;
         }
 
-        final float appScale = attachInfo.mApplicationScale;
-        final boolean scalingRequired = attachInfo.mScalingRequired;
+        final float appScale = mAttachInfo.mApplicationScale;
+        final boolean scalingRequired = mAttachInfo.mScalingRequired;
 
         int resizeAlpha = 0;
         if (mResizeBuffer != null) {
@@ -2427,7 +2421,7 @@
         }
 
         if (fullRedrawNeeded) {
-            attachInfo.mIgnoreDirtyState = true;
+            mAttachInfo.mIgnoreDirtyState = true;
             dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
         }
 
@@ -2440,7 +2434,7 @@
                     appScale + ", width=" + mWidth + ", height=" + mHeight);
         }
 
-        attachInfo.mTreeObserver.dispatchOnDraw();
+        mAttachInfo.mTreeObserver.dispatchOnDraw();
 
         int xOffset = 0;
         int yOffset = curScrollY;
@@ -2455,7 +2449,7 @@
         }
 
         if (!dirty.isEmpty() || mIsAnimating) {
-            if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
+            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                 // Draw with hardware renderer.
                 mIsAnimating = false;
                 if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
@@ -2468,7 +2462,7 @@
                 dirty.setEmpty();
 
                 mBlockResizeBuffer = false;
-                attachInfo.mHardwareRenderer.draw(mView, attachInfo, this);
+                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
             } else {
                 // If we get here with a disabled & requested hardware renderer, something went
                 // wrong (an invalidate posted right before we destroyed the hardware surface
@@ -2478,12 +2472,12 @@
                 // Before we request a new frame we must however attempt to reinitiliaze the
                 // hardware renderer if it's in requested state. This would happen after an
                 // eglTerminate() for instance.
-                if (attachInfo.mHardwareRenderer != null &&
-                        !attachInfo.mHardwareRenderer.isEnabled() &&
-                        attachInfo.mHardwareRenderer.isRequested()) {
+                if (mAttachInfo.mHardwareRenderer != null &&
+                        !mAttachInfo.mHardwareRenderer.isEnabled() &&
+                        mAttachInfo.mHardwareRenderer.isRequested()) {
 
                     try {
-                        attachInfo.mHardwareRenderer.initializeIfNeeded(
+                        mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                                 mWidth, mHeight, mSurface, surfaceInsets);
                     } catch (OutOfResourcesException e) {
                         handleOutOfResourcesException(e);
@@ -2495,7 +2489,7 @@
                     return;
                 }
 
-                if (!drawSoftware(surface, attachInfo, xOffset, yOffset, scalingRequired, dirty)) {
+                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                     return;
                 }
             }
@@ -2651,20 +2645,17 @@
     }
 
     private Drawable getAccessibilityFocusedDrawable() {
-        if (mAttachInfo != null) {
-            // Lazily load the accessibility focus drawable.
-            if (mAttachInfo.mAccessibilityFocusDrawable == null) {
-                TypedValue value = new TypedValue();
-                final boolean resolved = mView.mContext.getTheme().resolveAttribute(
-                        R.attr.accessibilityFocusedDrawable, value, true);
-                if (resolved) {
-                    mAttachInfo.mAccessibilityFocusDrawable =
+        // Lazily load the accessibility focus drawable.
+        if (mAttachInfo.mAccessibilityFocusDrawable == null) {
+            TypedValue value = new TypedValue();
+            final boolean resolved = mView.mContext.getTheme().resolveAttribute(
+                    R.attr.accessibilityFocusedDrawable, value, true);
+            if (resolved) {
+                mAttachInfo.mAccessibilityFocusDrawable =
                         mView.mContext.getDrawable(value.resourceId);
-                }
             }
-            return mAttachInfo.mAccessibilityFocusDrawable;
         }
-        return null;
+        return mAttachInfo.mAccessibilityFocusDrawable;
     }
 
     /**
@@ -2678,9 +2669,8 @@
     }
 
     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        final Rect ci = attachInfo.mContentInsets;
-        final Rect vi = attachInfo.mVisibleInsets;
+        final Rect ci = mAttachInfo.mContentInsets;
+        final Rect vi = mAttachInfo.mVisibleInsets;
         int scrollY = 0;
         boolean handled = false;
 
@@ -5147,12 +5137,11 @@
         if (args.localChanges != 0) {
             mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
         }
-        if (mAttachInfo != null) {
-            int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
-            if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
-                mAttachInfo.mGlobalSystemUiVisibility = visibility;
-                mView.dispatchSystemUiVisibilityChanged(visibility);
-            }
+
+        int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
+        if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
+            mAttachInfo.mGlobalSystemUiVisibility = visibility;
+            mView.dispatchSystemUiVisibilityChanged(visibility);
         }
     }
 
@@ -5485,8 +5474,7 @@
     }
 
     private void destroyHardwareRenderer() {
-        AttachInfo attachInfo = mAttachInfo;
-        HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer;
+        HardwareRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
 
         if (hardwareRenderer != null) {
             if (mView != null) {
@@ -5495,8 +5483,8 @@
             hardwareRenderer.destroy();
             hardwareRenderer.setRequested(false);
 
-            attachInfo.mHardwareRenderer = null;
-            attachInfo.mHardwareAccelerated = false;
+            mAttachInfo.mHardwareRenderer = null;
+            mAttachInfo.mHardwareAccelerated = false;
         }
     }
 
@@ -6197,37 +6185,35 @@
     }
 
     private View getCommonPredecessor(View first, View second) {
-        if (mAttachInfo != null) {
-            if (mTempHashSet == null) {
-                mTempHashSet = new HashSet<View>();
-            }
-            HashSet<View> seen = mTempHashSet;
-            seen.clear();
-            View firstCurrent = first;
-            while (firstCurrent != null) {
-                seen.add(firstCurrent);
-                ViewParent firstCurrentParent = firstCurrent.mParent;
-                if (firstCurrentParent instanceof View) {
-                    firstCurrent = (View) firstCurrentParent;
-                } else {
-                    firstCurrent = null;
-                }
-            }
-            View secondCurrent = second;
-            while (secondCurrent != null) {
-                if (seen.contains(secondCurrent)) {
-                    seen.clear();
-                    return secondCurrent;
-                }
-                ViewParent secondCurrentParent = secondCurrent.mParent;
-                if (secondCurrentParent instanceof View) {
-                    secondCurrent = (View) secondCurrentParent;
-                } else {
-                    secondCurrent = null;
-                }
-            }
-            seen.clear();
+        if (mTempHashSet == null) {
+            mTempHashSet = new HashSet<View>();
         }
+        HashSet<View> seen = mTempHashSet;
+        seen.clear();
+        View firstCurrent = first;
+        while (firstCurrent != null) {
+            seen.add(firstCurrent);
+            ViewParent firstCurrentParent = firstCurrent.mParent;
+            if (firstCurrentParent instanceof View) {
+                firstCurrent = (View) firstCurrentParent;
+            } else {
+                firstCurrent = null;
+            }
+        }
+        View secondCurrent = second;
+        while (secondCurrent != null) {
+            if (seen.contains(secondCurrent)) {
+                seen.clear();
+                return secondCurrent;
+            }
+            ViewParent secondCurrentParent = secondCurrent.mParent;
+            if (secondCurrentParent instanceof View) {
+                secondCurrent = (View) secondCurrentParent;
+            } else {
+                secondCurrent = null;
+            }
+        }
+        seen.clear();
         return null;
     }
 
@@ -6291,6 +6277,11 @@
         return false;
     }
 
+    @Override
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        return false;
+    }
+
     void changeCanvasOpacity(boolean opaque) {
         Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
         if (mAttachInfo.mHardwareRenderer != null) {
@@ -6635,6 +6626,9 @@
 
             // Destroy Displaylists so they can be recreated with high contrast recordings
             destroyHardwareResources();
+
+            // Schedule redraw, which will rerecord + redraw all text
+            invalidate();
         }
     }
 
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
new file mode 100644
index 0000000..a77459b
--- /dev/null
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.webkit;
+
+/**
+ * Private service to wait for the updatable WebView to be ready for use.
+ * @hide
+ */
+interface IWebViewUpdateService {
+
+    /**
+     * Used by the relro file creator to notify the service that it's done.
+     */
+    void notifyRelroCreationCompleted(boolean is64Bit, boolean success);
+
+    /**
+     * Used by WebViewFactory to block loading of WebView code until
+     * preparations are complete.
+     */
+    void waitForRelroCreationCompleted(boolean is64Bit);
+
+}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index aaf0a75..2c7b3eb 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,9 +16,18 @@
 
 package android.webkit;
 
+import android.os.Build;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+
+import com.android.internal.os.Zygote;
 
 /**
  * Top level factory, used creating all the main WebView implementation classes.
@@ -33,25 +42,31 @@
     private static final String NULL_WEBVIEW_FACTORY =
             "com.android.webview.nullwebview.NullWebViewFactoryProvider";
 
+    // TODO(torne): we need to use a system property instead of hardcoding the library paths to
+    // enable it to be changed when a webview update apk is installed.
+    private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_32 =
+            "/system/lib/libwebviewchromium.so";
+    private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_64 =
+            "/system/lib64/libwebviewchromium.so";
+    private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
+            "/data/misc/shared_relro/libwebviewchromium32.relro";
+    private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
+            "/data/misc/shared_relro/libwebviewchromium64.relro";
+
     private static final String LOGTAG = "WebViewFactory";
 
     private static final boolean DEBUG = false;
 
-    private static class Preloader {
-        static WebViewFactoryProvider sPreloadedProvider;
-        static {
-            try {
-                sPreloadedProvider = getFactoryClass().newInstance();
-            } catch (Exception e) {
-                Log.w(LOGTAG, "error preloading provider", e);
-            }
-        }
-    }
-
     // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
     // same provider.
     private static WebViewFactoryProvider sProviderInstance;
     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) {
@@ -59,6 +74,8 @@
             // us honest and minimize usage of WebView internals when binding the proxy.
             if (sProviderInstance != null) return sProviderInstance;
 
+            loadNativeLibrary();
+
             Class<WebViewFactoryProvider> providerClass;
             try {
                 providerClass = getFactoryClass();
@@ -67,15 +84,6 @@
                 throw new AndroidRuntimeException(e);
             }
 
-            // This implicitly loads Preloader even if it wasn't preloaded at boot.
-            if (Preloader.sPreloadedProvider != null &&
-                Preloader.sPreloadedProvider.getClass() == providerClass) {
-                sProviderInstance = Preloader.sPreloadedProvider;
-                if (DEBUG) Log.v(LOGTAG, "Using preloaded provider: " + sProviderInstance);
-                return sProviderInstance;
-            }
-
-            // The preloaded provider isn't the one we wanted; construct our own.
             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
             try {
                 sProviderInstance = providerClass.newInstance();
@@ -98,4 +106,121 @@
             return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
         }
     }
+
+    /**
+     * Perform any WebView loading preparations that must happen in the zygote.
+     * Currently, this means allocating address space to load the real JNI library later.
+     */
+    public static void prepareWebViewInZygote() {
+        try {
+            System.loadLibrary("webviewchromium_loader");
+            sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
+                                                              CHROMIUM_WEBVIEW_NATIVE_LIB_64);
+            if (sAddressSpaceReserved) {
+                if (DEBUG) Log.v(LOGTAG, "address space reserved");
+            } else {
+                Log.e(LOGTAG, "reserving address space failed");
+            }
+        } catch (Throwable e) {
+            // Log and discard errors at this stage as we must not crash the zygote.
+            Log.e(LOGTAG, "error preparing native loader", e);
+        }
+    }
+
+    /**
+     * Perform any WebView loading preparations that must happen at boot from the system server,
+     * after the package manager has started.
+     * This must be called in the system server.
+     * Currently, this means spawning the child processes which will create the relro files.
+     */
+    public static void prepareWebViewInSystemServer() {
+        if (DEBUG) Log.v(LOGTAG, "creating relro files");
+        if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) {
+            createRelroFile(Build.SUPPORTED_64_BIT_ABIS[0]);
+        }
+        if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) {
+            createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]);
+        }
+    }
+
+    private static void createRelroFile(String abi) {
+        try {
+            Process.start("android.webkit.WebViewFactory$RelroFileCreator",
+                          "WebViewLoader-" + abi,
+                          Process.SHARED_RELRO_UID,
+                          Process.SHARED_RELRO_UID,
+                          null,
+                          0,                 // TODO(torne): do we need to set debug flags?
+                          Zygote.MOUNT_EXTERNAL_NONE,
+                          Build.VERSION.SDK_INT,
+                          null,
+                          abi,
+                          null);
+        } catch (Throwable e) {
+            // Log and discard errors as we must not crash the system server.
+            Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e);
+        }
+    }
+
+    private static class RelroFileCreator {
+        // Called in an unprivileged child process to create the relro file.
+        public static void main(String[] args) {
+            if (!sAddressSpaceReserved) {
+                Log.e(LOGTAG, "can't create relro file; address space not reserved");
+                return;
+            }
+            boolean result = nativeCreateRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
+                                                   CHROMIUM_WEBVIEW_NATIVE_LIB_64,
+                                                   CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+                                                   CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+            if (!result) {
+                Log.e(LOGTAG, "failed to create relro file");
+            } else if (DEBUG) {
+                Log.v(LOGTAG, "created relro file");
+            }
+            try {
+                getUpdateService().notifyRelroCreationCompleted(VMRuntime.getRuntime().is64Bit(),
+                                                                result);
+            } catch (RemoteException e) {
+                Log.e(LOGTAG, "error notifying update service", e);
+            }
+
+            // Must explicitly exit or else this process will just sit around after we return.
+            System.exit(0);
+        }
+    }
+
+    private static void loadNativeLibrary() {
+        if (!sAddressSpaceReserved) {
+            Log.e(LOGTAG, "can't load with relro file; address space not reserved");
+            return;
+        }
+
+        try {
+            getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit());
+        } catch (RemoteException e) {
+            Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e);
+            return;
+        }
+
+        boolean result = nativeLoadWithRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
+                                                 CHROMIUM_WEBVIEW_NATIVE_LIB_64,
+                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+        if (!result) {
+            Log.w(LOGTAG, "failed to load with relro file, proceeding without");
+        } else if (DEBUG) {
+            Log.v(LOGTAG, "loaded with relro file");
+        }
+    }
+
+    private static IWebViewUpdateService getUpdateService() {
+        return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
+    }
+
+    private static native boolean nativeReserveAddressSpace(String lib32, String lib64);
+    private static native boolean nativeCreateRelroFile(String lib32, String lib64,
+                                                        String relro32, String relro64);
+    private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
+                                                          String relro32, String relro64);
 }
diff --git a/core/java/android/widget/LegacyTimePickerDelegate.java b/core/java/android/widget/LegacyTimePickerDelegate.java
index 2216003..6dd70ba 100644
--- a/core/java/android/widget/LegacyTimePickerDelegate.java
+++ b/core/java/android/widget/LegacyTimePickerDelegate.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.format.DateFormat;
@@ -104,14 +105,13 @@
         super(delegator, context);
 
         // process style attributes
-        final TypedArray attributesArray = mContext.obtainStyledAttributes(
+        final TypedArray a = mContext.obtainStyledAttributes(
                 attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
-        final int layoutResourceId = attributesArray.getResourceId(
+        final int layoutResourceId = a.getResourceId(
                 R.styleable.TimePicker_legacyLayout, R.layout.time_picker_legacy);
-        attributesArray.recycle();
+        a.recycle();
 
-        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
         inflater.inflate(layoutResourceId, mDelegator, true);
 
         // hour
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 883183e..9b763c1 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -36,6 +36,7 @@
 import android.text.format.Time;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.View;
@@ -190,6 +191,8 @@
     private int mAmOrPm;
     private int mAmOrPmPressed;
 
+    private int mDisabledAlpha;
+
     private RectF mRectF = new RectF();
     private boolean mInputEnabled = true;
     private OnValueSelectedListener mListener;
@@ -310,15 +313,18 @@
     public RadialTimePickerView(Context context, AttributeSet attrs, int defStyle)  {
         super(context, attrs);
 
+        // Pull disabled alpha from theme.
+        final TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
+        mDisabledAlpha = (int) (outValue.getFloat() * 255 + 0.5f);
+
         // process style attributes
+        final Resources res = getResources();
         final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TimePicker,
                 defStyle, 0);
 
-        final Resources res = getResources();
-
         mAmPmUnselectedColor = a.getColor(R.styleable.TimePicker_amPmUnselectedBackgroundColor,
                 res.getColor(R.color.timepicker_default_ampm_unselected_background_color_material));
-
         mAmPmSelectedColor = a.getColor(R.styleable.TimePicker_amPmSelectedBackgroundColor,
                 res.getColor(R.color.timepicker_default_ampm_selected_background_color_material));
 
@@ -406,10 +412,6 @@
                 res.getColor(R.color.timepicker_default_numbers_background_color_material)));
         mPaintBackground.setAntiAlias(true);
 
-        mPaintDisabled.setColor(a.getColor(R.styleable.TimePicker_disabledColor,
-                res.getColor(R.color.timepicker_default_disabled_color_material)));
-        mPaintDisabled.setAntiAlias(true);
-
         if (DEBUG) {
             mPaintDebug.setColor(DEBUG_COLOR);
             mPaintDebug.setAntiAlias(true);
@@ -722,7 +724,11 @@
 
     @Override
     public void onDraw(Canvas canvas) {
-        canvas.save();
+        if (!mInputEnabled) {
+            canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), mDisabledAlpha);
+        } else {
+            canvas.save();
+        }
 
         calculateGridSizesHours();
         calculateGridSizesMinutes();
@@ -749,12 +755,6 @@
             drawAmPm(canvas);
         }
 
-        if(!mInputEnabled) {
-            // Draw outer view rectangle
-            mRectF.set(0, 0, getWidth(), getHeight());
-            canvas.drawRect(mRectF, mPaintDisabled);
-        }
-
         if (DEBUG) {
             drawDebug(canvas);
         }
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 5021960..58a6562 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -17,7 +17,6 @@
 package android.widget;
 
 import android.annotation.Widget;
-import android.app.UiModeManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
@@ -30,8 +29,6 @@
 
 import java.util.Locale;
 
-import static android.os.Build.VERSION_CODES.L;
-
 /**
  * A view for selecting the time of day, in either 24 hour or AM/PM mode. The
  * hour, each minute digit, and AM/PM (if applicable) can be conrolled by
@@ -41,20 +38,14 @@
  * by entering single digits. Under AM/PM mode, the user can hit 'a', 'A", 'p'
  * or 'P' to pick. For a dialog using this view, see
  * {@link android.app.TimePickerDialog}.
- *<p>
+ * <p>
  * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
  * guide.
  * </p>
  */
 @Widget
 public class TimePicker extends FrameLayout {
-
-    private TimePickerDelegate mDelegate;
-
-    private AttributeSet mAttrs;
-    private int mDefStyleAttr;
-    private int mDefStyleRes;
-    private Context mContext;
+    private final TimePickerDelegate mDelegate;
 
     /**
      * The callback interface used to indicate the time has been adjusted.
@@ -84,50 +75,18 @@
     public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mContext = context;
-        mAttrs = attrs;
-        mDefStyleAttr = defStyleAttr;
-        mDefStyleRes = defStyleRes;
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimePicker,
-                mDefStyleAttr, mDefStyleRes);
-
-        // Create the correct UI delegate. Legacy mode is used when API Levels is below L
-        // release or when it is a TV UI
-        final boolean isLegacyMode =  a.getBoolean(
-                R.styleable.TimePicker_legacyMode, isLegacyMode());
-
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
+        final boolean legacyMode = a.getBoolean(R.styleable.TimePicker_legacyMode, true);
         a.recycle();
 
-        setLegacyMode(isLegacyMode);
-    }
-
-    private boolean isLegacyMode() {
-        UiModeManager uiModeManager =
-                (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
-        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
-            return true;
+        if (legacyMode) {
+            mDelegate = new LegacyTimePickerDelegate(
+                    this, context, attrs, defStyleAttr, defStyleRes);
+        } else {
+            mDelegate = new android.widget.TimePickerDelegate(
+                    this, context, attrs, defStyleAttr, defStyleRes);
         }
-        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
-        return targetSdkVersion < L;
-    }
-
-    private TimePickerDelegate createLegacyUIDelegate(Context context, AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        return new LegacyTimePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    private TimePickerDelegate createNewUIDelegate(Context context, AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        return new android.widget.TimePickerDelegate(this, context, attrs, defStyleAttr,
-                defStyleRes);
-    }
-
-    private void setLegacyMode(boolean isLegacyMode) {
-        removeAllViewsInLayout();
-        mDelegate = isLegacyMode ?
-                createLegacyUIDelegate(mContext, mAttrs, mDefStyleAttr, mDefStyleRes) :
-                createNewUIDelegate(mContext, mAttrs, mDefStyleAttr, mDefStyleRes);
     }
 
     /**
@@ -185,9 +144,6 @@
 
     @Override
     public void setEnabled(boolean enabled) {
-        if (mDelegate.isEnabled() == enabled) {
-            return;
-        }
         super.setEnabled(enabled);
         mDelegate.setEnabled(enabled);
     }
diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerDelegate.java
index cd89667..a9af2f9 100644
--- a/core/java/android/widget/TimePickerDelegate.java
+++ b/core/java/android/widget/TimePickerDelegate.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -30,7 +31,6 @@
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -79,9 +79,6 @@
     // Duration in ms of the pulse animation
     private static final int PULSE_ANIMATOR_DURATION = 544;
 
-    private static int[] TEXT_APPEARANCE_TIME_LABEL_ATTR =
-            new int[] { R.attr.timePickerHeaderTimeLabelTextAppearance };
-
     private final View mMainView;
     private TextView mHourView;
     private TextView mMinuteView;
@@ -92,7 +89,7 @@
     private ViewGroup mLayoutButtons;
 
     private int mHeaderSelectedColor;
-    private int mHeaderUnSelectedColor;
+    private int mHeaderUnselectedColor;
     private String mAmText;
     private String mPmText;
 
@@ -140,17 +137,14 @@
         mSelectMinutes = res.getString(R.string.select_minutes);
 
         mHeaderSelectedColor = a.getColor(R.styleable.TimePicker_headerSelectedTextColor,
-                R.color.timepicker_default_selector_color_material);
+                res.getColor(R.color.timepicker_default_selector_color_material));
 
-        mHeaderUnSelectedColor = getUnselectedColor(R.color.timepicker_default_text_color_material);
-        if (mHeaderUnSelectedColor == -1) {
-            mHeaderUnSelectedColor = a.getColor(R.styleable.TimePicker_headerUnselectedTextColor,
-                    R.color.timepicker_default_text_color_material);
-        }
-
+        final int headerTimeTextAppearance = a.getResourceId(
+                R.styleable.TimePicker_headerTimeTextAppearance, 0);
+        final int headerAmPmTextAppearance = a.getResourceId(
+                R.styleable.TimePicker_headerAmPmTextAppearance, 0);
         final int headerBackgroundColor = a.getColor(
-                R.styleable.TimePicker_headerBackgroundColor, 0);
-
+                R.styleable.TimePicker_headerBackgroundColor, Color.TRANSPARENT);
         final int layoutResourceId = a.getResourceId(
                 R.styleable.TimePicker_internalLayout, R.layout.time_picker_holo);
 
@@ -162,18 +156,32 @@
         mMainView = inflater.inflate(layoutResourceId, null);
         mDelegator.addView(mMainView);
 
-        if (headerBackgroundColor != 0) {
-            RelativeLayout header = (RelativeLayout) mMainView.findViewById(R.id.time_header);
-            header.setBackgroundColor(headerBackgroundColor);
-        }
-
         mHourView = (TextView) mMainView.findViewById(R.id.hours);
+        mSeparatorView = (TextView) mMainView.findViewById(R.id.separator);
         mMinuteView = (TextView) mMainView.findViewById(R.id.minutes);
         mAmPmTextView = (TextView) mMainView.findViewById(R.id.ampm_label);
-        mSeparatorView = (TextView) mMainView.findViewById(R.id.separator);
-        mRadialTimePickerView = (RadialTimePickerView) mMainView.findViewById(R.id.radial_picker);
-
         mLayoutButtons = (ViewGroup) mMainView.findViewById(R.id.layout_buttons);
+
+        // Set up text appearances from style.
+        if (headerTimeTextAppearance != 0) {
+            mHourView.setTextAppearance(context, headerTimeTextAppearance);
+            mSeparatorView.setTextAppearance(context, headerTimeTextAppearance);
+            mMinuteView.setTextAppearance(context, headerTimeTextAppearance);
+        }
+
+        if (headerAmPmTextAppearance != 0) {
+            mAmPmTextView.setTextAppearance(context, headerAmPmTextAppearance);
+        }
+
+        if (headerBackgroundColor != Color.TRANSPARENT) {
+            mLayoutButtons.setBackgroundColor(headerBackgroundColor);
+            mMainView.findViewById(R.id.time_header).setBackgroundColor(headerBackgroundColor);
+        }
+
+        // Load unselected header color from current state.
+        mHeaderUnselectedColor = mHourView.getCurrentTextColor();
+
+        mRadialTimePickerView = (RadialTimePickerView) mMainView.findViewById(R.id.radial_picker);
         mDoneButton = (Button) mMainView.findViewById(R.id.done_button);
 
         String[] amPmTexts = new DateFormatSymbols().getAmPmStrings();
@@ -198,25 +206,6 @@
         initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX, false);
     }
 
-    private int getUnselectedColor(int defColor) {
-        int result = -1;
-        final Resources.Theme theme = mContext.getTheme();
-        final TypedValue outValue = new TypedValue();
-        theme.resolveAttribute(R.attr.timePickerHeaderTimeLabelTextAppearance, outValue, true);
-        final int appearanceResId = outValue.resourceId;
-        TypedArray appearance = null;
-        if (appearanceResId != -1) {
-            appearance = theme.obtainStyledAttributes(appearanceResId,
-                    com.android.internal.R.styleable.TextAppearance);
-        }
-        if (appearance != null) {
-            result = appearance.getColor(
-                    com.android.internal.R.styleable.TextAppearance_textColor, defColor);
-            appearance.recycle();
-        }
-        return result;
-    }
-
     private void initialize(int hourOfDay, int minute, boolean is24HourView, int index,
                             boolean showDoneButton) {
         mInitialHourOfDay = hourOfDay;
@@ -292,11 +281,7 @@
 
     private int computeMaxWidthOfNumbers(int max) {
         TextView tempView = new TextView(mContext);
-        TypedArray a = mContext.obtainStyledAttributes(TEXT_APPEARANCE_TIME_LABEL_ATTR);
-        final int textAppearanceResId = a.getResourceId(0, 0);
-        tempView.setTextAppearance(mContext, (textAppearanceResId != 0) ?
-                textAppearanceResId : R.style.TextAppearance_Material_TimePicker_TimeLabel);
-        a.recycle();
+        tempView.setTextAppearance(mContext, R.style.TextAppearance_Material_TimePicker_TimeLabel);
         ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT);
         tempView.setLayoutParams(lp);
@@ -799,6 +784,7 @@
             separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
         }
         mSeparatorView.setText(separatorText);
+        mSeparatorView.setTextColor(mHeaderUnselectedColor);
     }
 
     static private int lastIndexOfAny(String str, char[] any) {
@@ -852,8 +838,8 @@
             labelToAnimate = mMinuteView;
         }
 
-        int hourColor = (index == HOUR_INDEX) ? mHeaderSelectedColor : mHeaderUnSelectedColor;
-        int minuteColor = (index == MINUTE_INDEX) ? mHeaderSelectedColor : mHeaderUnSelectedColor;
+        int hourColor = (index == HOUR_INDEX) ? mHeaderSelectedColor : mHeaderUnselectedColor;
+        int minuteColor = (index == MINUTE_INDEX) ? mHeaderSelectedColor : mHeaderUnselectedColor;
         mHourView.setTextColor(hourColor);
         mMinuteView.setTextColor(minuteColor);
 
@@ -1077,9 +1063,9 @@
             String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
                     String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
             mHourView.setText(hourStr);
-            mHourView.setTextColor(mHeaderUnSelectedColor);
+            mHourView.setTextColor(mHeaderUnselectedColor);
             mMinuteView.setText(minuteStr);
-            mMinuteView.setTextColor(mHeaderUnSelectedColor);
+            mMinuteView.setTextColor(mHeaderUnselectedColor);
             if (!mIs24HourView) {
                 updateAmPmDisplay(values[2]);
             }
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 247c8fe..cfeca08 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -80,6 +80,15 @@
 
     @Override
     public int compareTo(BatterySipper other) {
+        // Over-counted always goes to the bottom.
+        if (drainType != other.drainType) {
+            if (drainType == DrainType.OVERCOUNTED) {
+                // This is "larger"
+                return 1;
+            } else if (other.drainType == DrainType.OVERCOUNTED) {
+                return -1;
+            }
+        }
         // Return the flipped value because we want the items in descending order
         return Double.compare(other.value, value);
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 023ba03..6c9b4b8 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -48,7 +48,6 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
-import java.util.Arrays;
 
 /**
  * A helper class for retrieving the power usage information for all applications and services.
@@ -95,6 +94,7 @@
 
     private long mStatsPeriod = 0;
     private double mMaxPower = 1;
+    private double mMaxRealPower = 1;
     private double mComputedPower;
     private double mTotalPower;
     private double mWifiPower;
@@ -208,6 +208,7 @@
         getStats();
 
         mMaxPower = 0;
+        mMaxRealPower = 0;
         mComputedPower = 0;
         mTotalPower = 0;
         mWifiPower = 0;
@@ -542,6 +543,7 @@
                 } else {
                     mUsageList.add(app);
                     if (power > mMaxPower) mMaxPower = power;
+                    if (power > mMaxRealPower) mMaxRealPower = power;
                     mComputedPower += power;
                 }
                 if (u.getUid() == 0) {
@@ -567,6 +569,7 @@
                 osApp.value += power;
                 osApp.values[0] += power;
                 if (osApp.value > mMaxPower) mMaxPower = osApp.value;
+                if (osApp.value > mMaxRealPower) mMaxRealPower = osApp.value;
                 mComputedPower += power;
             }
         }
@@ -806,6 +809,7 @@
 
     private BatterySipper addEntry(DrainType drainType, long time, double power) {
         mComputedPower += power;
+        if (power > mMaxRealPower) mMaxRealPower = power;
         return addEntryNoTotal(drainType, time, power);
     }
 
@@ -831,6 +835,8 @@
 
     public double getMaxPower() { return mMaxPower; }
 
+    public double getMaxRealPower() { return mMaxRealPower; }
+
     public double getTotalPower() { return mTotalPower; }
 
     public double getComputedPower() { return mComputedPower; }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5ce658b..eea4201 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -34,6 +34,7 @@
 import android.system.OsConstants;
 import android.util.EventLog;
 import android.util.Log;
+import android.webkit.WebViewFactory;
 
 import dalvik.system.VMRuntime;
 
@@ -250,6 +251,9 @@
         preloadClasses();
         preloadResources();
         preloadOpenGL();
+        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
+        // for memory sharing purposes.
+        WebViewFactory.prepareWebViewInZygote();
         Log.d(TAG, "end preload");
     }
 
diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java
index ff905bb..0dde465 100644
--- a/core/java/com/android/server/net/NetlinkTracker.java
+++ b/core/java/com/android/server/net/NetlinkTracker.java
@@ -79,7 +79,7 @@
     private final LinkProperties mLinkProperties;
     private DnsServerRepository mDnsServerRepository;
 
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
 
     public NetlinkTracker(String iface, Callback callback) {
         TAG = "NetlinkTracker/" + iface;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index f65aab5..0e22174 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -90,12 +90,12 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
-	android_graphics_Canvas.cpp \
 	android_graphics_Picture.cpp \
 	android/graphics/AutoDecodeCancel.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
 	android/graphics/Camera.cpp \
+	android/graphics/Canvas.cpp \
 	android/graphics/CanvasProperty.cpp \
 	android/graphics/ColorFilter.cpp \
 	android/graphics/DrawFilter.cpp \
@@ -122,7 +122,6 @@
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
-	android/graphics/SkiaCanvas.cpp \
 	android/graphics/SurfaceTexture.cpp \
 	android/graphics/Typeface.cpp \
 	android/graphics/TypefaceImpl.cpp \
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 9f832b0..d17f46c 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,7 +3,6 @@
 
 #include "SkCamera.h"
 
-#include "Canvas.h"
 #include "GraphicsJNI.h"
 
 static jfieldID gNativeInstanceFieldID;
@@ -96,10 +95,10 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+    SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas(canvas);
+    v->applyToCanvas((SkCanvas*)native_canvas);
 }
 
 static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
new file mode 100644
index 0000000..6254f3d
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -0,0 +1,1330 @@
+/*
+ * Copyright (C) 2006-2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkClipStack.h"
+#include "SkDevice.h"
+#include "SkDeque.h"
+#include "SkDrawFilter.h"
+#include "SkGraphics.h"
+#include <SkImageInfo.h>
+#include "SkPorterDuff.h"
+#include "SkShader.h"
+#include "SkTArray.h"
+#include "SkTemplates.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+#include "TypefaceImpl.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipRect(rect, op, antialias);
+    }
+    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipRRect(rrect, op, antialias);
+    }
+    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipPath(path, op, antialias);
+    }
+
+private:
+    SkCanvas* m_dstCanvas;
+};
+
+// Holds an SkCanvas reference plus additional native data.
+class NativeCanvasWrapper {
+private:
+    struct SaveRec {
+        int                 saveCount;
+        SkCanvas::SaveFlags saveFlags;
+    };
+
+public:
+    NativeCanvasWrapper(SkCanvas* canvas)
+        : mCanvas(canvas)
+        , mSaveStack(NULL) {
+        SkASSERT(canvas);
+    }
+
+    ~NativeCanvasWrapper() {
+        delete mSaveStack;
+    }
+
+    SkCanvas* getCanvas() const {
+        return mCanvas.get();
+    }
+
+    void setCanvas(SkCanvas* canvas) {
+        SkASSERT(canvas);
+        mCanvas.reset(canvas);
+
+        delete mSaveStack;
+        mSaveStack = NULL;
+    }
+
+    int save(SkCanvas::SaveFlags flags) {
+        int count = mCanvas->save();
+        recordPartialSave(flags);
+        return count;
+    }
+
+    int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                            SkCanvas::SaveFlags flags) {
+        int count = mCanvas->saveLayer(bounds, paint,
+                static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
+        recordPartialSave(flags);
+        return count;
+    }
+
+    int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+                       SkCanvas::SaveFlags flags) {
+        int count = mCanvas->saveLayerAlpha(bounds, alpha,
+                static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
+        recordPartialSave(flags);
+        return count;
+    }
+
+    void restore() {
+        const SaveRec* rec = (NULL == mSaveStack)
+                ? NULL
+                : static_cast<SaveRec*>(mSaveStack->back());
+        int currentSaveCount = mCanvas->getSaveCount() - 1;
+        SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+        if (NULL == rec || rec->saveCount != currentSaveCount) {
+            // Fast path - no record for this frame.
+            mCanvas->restore();
+            return;
+        }
+
+        bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+        bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+        SkMatrix savedMatrix;
+        if (preserveMatrix) {
+            savedMatrix = mCanvas->getTotalMatrix();
+        }
+
+        SkTArray<SkClipStack::Element> savedClips;
+        if (preserveClip) {
+            saveClipsForFrame(savedClips, currentSaveCount);
+        }
+
+        mCanvas->restore();
+
+        if (preserveMatrix) {
+            mCanvas->setMatrix(savedMatrix);
+        }
+
+        if (preserveClip && !savedClips.empty()) {
+            applyClips(savedClips);
+        }
+
+        mSaveStack->pop_back();
+    }
+
+private:
+    void recordPartialSave(SkCanvas::SaveFlags flags) {
+        // A partial save is a save operation which doesn't capture the full canvas state.
+        // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+        // Mask-out non canvas state bits.
+        flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+        if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+            // not a partial save.
+            return;
+        }
+
+        if (NULL == mSaveStack) {
+            mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
+        }
+
+        SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+        // Store the save counter in the SkClipStack domain.
+        // (0-based, equal to the number of save ops on the stack).
+        rec->saveCount = mCanvas->getSaveCount() - 1;
+        rec->saveFlags = flags;
+    }
+
+    void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
+                           int frameSaveCount) {
+        SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+                                       SkClipStack::Iter::kTop_IterStart);
+        while (const SkClipStack::Element* elem = clipIterator.next()) {
+            if (elem->getSaveCount() < frameSaveCount) {
+                // done with the current frame.
+                break;
+            }
+            SkASSERT(elem->getSaveCount() == frameSaveCount);
+            clips.push_back(*elem);
+        }
+    }
+
+    void applyClips(const SkTArray<SkClipStack::Element>& clips) {
+        ClipCopier clipCopier(mCanvas);
+
+        // The clip stack stores clips in device space.
+        SkMatrix origMatrix = mCanvas->getTotalMatrix();
+        mCanvas->resetMatrix();
+
+        // We pushed the clips in reverse order.
+        for (int i = clips.count() - 1; i >= 0; --i) {
+            clips[i].replay(&clipCopier);
+        }
+
+        mCanvas->setMatrix(origMatrix);
+    }
+
+    SkAutoTUnref<SkCanvas> mCanvas;
+    SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
+};
+
+// Returns true if the SkCanvas's clip is non-empty.
+static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
+    bool emptyClip = canvas.isClipEmpty();
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+class SkCanvasGlue {
+public:
+    // Get the native wrapper for a given handle.
+    static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
+        SkASSERT(nativeHandle);
+        return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+    }
+
+    // Get the SkCanvas for a given native handle.
+    static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
+        NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
+        SkCanvas* canvas = wrapper->getCanvas();
+        SkASSERT(canvas);
+
+        return canvas;
+    }
+
+    // Construct an SkCanvas from the bitmap.
+    static SkCanvas* createCanvas(SkBitmap* bitmap) {
+        if (bitmap) {
+            return SkNEW_ARGS(SkCanvas, (*bitmap));
+        }
+
+        // Create an empty bitmap device to prevent callers from crashing
+        // if they attempt to draw into this canvas.
+        SkBitmap emptyBitmap;
+        return new SkCanvas(emptyBitmap);
+    }
+
+    // Copy the canvas matrix & clip state.
+    static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
+        if (srcCanvas && dstCanvas) {
+            dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
+            if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
+                ClipCopier copier(dstCanvas);
+                srcCanvas->replayClips(&copier);
+            }
+        }
+    }
+
+    // Native JNI handlers
+    static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
+        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+        delete wrapper;
+    }
+
+    // Native wrapper constructor used by Canvas(Bitmap)
+    static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+        // No check - 0 is a valid bitmapHandle.
+        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        SkCanvas* canvas = createCanvas(bitmap);
+
+        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+    }
+
+    // Native wrapper constructor used by Canvas(native_canvas)
+    static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
+        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
+        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
+    }
+
+    // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+    // optionally copying canvas matrix & clip state.
+    static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                          jboolean copyState) {
+        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
+        SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
+        NPE_CHECK_RETURN_VOID(env, newCanvas);
+
+        if (copyState == JNI_TRUE) {
+            copyCanvasState(wrapper->getCanvas(), newCanvas);
+        }
+
+        // setCanvas() unrefs the old canvas.
+        wrapper->setCanvas(newCanvas);
+    }
+
+    static void freeCaches(JNIEnv* env, jobject) {
+        SkGraphics::PurgeFontCache();
+    }
+
+    static void freeTextLayoutCaches(JNIEnv* env, jobject) {
+        Layout::purgeCaches();
+    }
+
+    static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        bool result = canvas->getDevice()->accessBitmap(false).isOpaque();
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        int width = canvas->getDevice()->accessBitmap(false).width();
+        return static_cast<jint>(width);
+    }
+
+    static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        int height = canvas->getDevice()->accessBitmap(false).height();
+        return static_cast<jint>(height);
+    }
+
+    static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+        return static_cast<jint>(wrapper->save(flags));
+    }
+
+    static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
+                          jfloat l, jfloat t, jfloat r, jfloat b,
+                          jlong paintHandle, jint flagsHandle) {
+        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+        SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
+        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+        SkRect bounds;
+        bounds.set(l, t, r, b);
+        return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
+    }
+
+    static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
+                               jfloat l, jfloat t, jfloat r, jfloat b,
+                               jint alpha, jint flagsHandle) {
+        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+        SkRect  bounds;
+        bounds.set(l, t, r, b);
+        return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
+    }
+
+    static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+        if (wrapper->getCanvas()->getSaveCount() <= 1) {  // cannot restore anymore
+            doThrowISE(env, "Underflow in restore");
+            return;
+        }
+        wrapper->restore();
+    }
+
+    static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
+        return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
+    }
+
+    static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
+                               jint restoreCount) {
+        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+        if (restoreCount < 1) {
+            doThrowIAE(env, "Underflow in restoreToCount");
+            return;
+        }
+
+        while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
+            wrapper->restore();
+        }
+    }
+
+    static void translate(JNIEnv*, jobject, jlong canvasHandle,
+                          jfloat dx, jfloat dy) {
+        getNativeCanvas(canvasHandle)->translate(dx, dy);
+    }
+
+    static void scale__FF(JNIEnv*, jobject, jlong canvasHandle,
+                          jfloat sx, jfloat sy) {
+        getNativeCanvas(canvasHandle)->scale(sx, sy);
+    }
+
+    static void rotate__F(JNIEnv*, jobject, jlong canvasHandle,
+                          jfloat degrees) {
+        getNativeCanvas(canvasHandle)->rotate(degrees);
+    }
+
+    static void skew__FF(JNIEnv*, jobject, jlong canvasHandle,
+                         jfloat sx, jfloat sy) {
+        getNativeCanvas(canvasHandle)->skew(sx, sy);
+    }
+
+    static void concat(JNIEnv* env, jobject, jlong canvasHandle,
+                       jlong matrixHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        canvas->concat(*matrix);
+    }
+
+    static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
+                          jlong matrixHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        if (NULL == matrix) {
+            canvas->resetMatrix();
+        } else {
+            canvas->setMatrix(*matrix);
+        }
+    }
+
+    static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle,
+                                  jfloat left, jfloat top, jfloat right,
+                                  jfloat bottom, jint op) {
+        SkRect  r;
+        r.set(left, top, right, bottom);
+        SkCanvas* c = getNativeCanvas(canvasHandle);
+        c->clipRect(r, static_cast<SkRegion::Op>(op));
+        return hasNonEmptyClip(*c);
+    }
+
+    static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
+                             jlong pathHandle, jint op) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
+                static_cast<SkRegion::Op>(op));
+        return hasNonEmptyClip(*canvas);
+    }
+
+    static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
+                               jlong deviceRgnHandle, jint op) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
+        SkPath rgnPath;
+        if (deviceRgn->getBoundaryPath(&rgnPath)) {
+            // The region is specified in device space.
+            SkMatrix savedMatrix = canvas->getTotalMatrix();
+            canvas->resetMatrix();
+            canvas->clipPath(rgnPath, static_cast<SkRegion::Op>(op));
+            canvas->setMatrix(savedMatrix);
+        } else {
+            canvas->clipRect(SkRect::MakeEmpty(), static_cast<SkRegion::Op>(op));
+        }
+        return hasNonEmptyClip(*canvas);
+    }
+
+    static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
+                              jlong filterHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+    }
+
+    static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
+                                       jlong pathHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
+                                       jfloat left, jfloat top, jfloat right,
+                                       jfloat bottom) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkRect r;
+        r.set(left, top, right, bottom);
+        bool result = canvas->quickReject(r);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
+                        jint r, jint g, jint b) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        canvas->drawARGB(0xFF, r, g, b);
+    }
+
+    static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
+                         jint a, jint r, jint g, jint b) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        canvas->drawARGB(a, r, g, b);
+    }
+
+    static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
+                             jint color) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        canvas->drawColor(color);
+    }
+
+    static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
+                              jint color, jint modeHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
+        canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
+    }
+
+    static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
+                          jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawPaint(*paint);
+    }
+
+    static void doPoints(JNIEnv* env, jlong canvasHandle,
+                         jfloatArray jptsArray, jint offset, jint count,
+                         jlong paintHandle, jint modeHandle) {
+        NPE_CHECK_RETURN_VOID(env, jptsArray);
+        SkCanvas::PointMode mode = static_cast<SkCanvas::PointMode>(modeHandle);
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+        AutoJavaFloatArray autoPts(env, jptsArray);
+        float* floats = autoPts.ptr();
+        const int length = autoPts.length();
+
+        if ((offset | count) < 0 || offset + count > length) {
+            doThrowAIOOBE(env);
+            return;
+        }
+
+        // now convert the floats into SkPoints
+        count >>= 1;    // now it is the number of points
+        SkAutoSTMalloc<32, SkPoint> storage(count);
+        SkPoint* pts = storage.get();
+        const float* src = floats + offset;
+        for (int i = 0; i < count; i++) {
+            pts[i].set(src[0], src[1]);
+            src += 2;
+        }
+        canvas->drawPoints(mode, count, pts, *paint);
+    }
+
+    static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle,
+                           jfloatArray jptsArray, jint offset,
+                           jint count, jlong paintHandle) {
+        doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
+                 SkCanvas::kPoints_PointMode);
+    }
+
+    static void drawLines(JNIEnv* env, jobject, jlong canvasHandle,
+                          jfloatArray jptsArray, jint offset, jint count,
+                          jlong paintHandle) {
+        doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
+                 SkCanvas::kLines_PointMode);
+    }
+
+    static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+                          jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawPoint(x, y, *paint);
+    }
+
+    static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
+                                    jfloat startX, jfloat startY, jfloat stopX,
+                                    jfloat stopY, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawLine(startX, startY, stopX, stopY, *paint);
+    }
+
+    static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
+                                    jfloat left, jfloat top, jfloat right,
+                                    jfloat bottom, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawRectCoords(left, top, right, bottom, *paint);
+    }
+
+    static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+            jfloat right, jfloat bottom, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+        canvas->drawOval(oval, *paint);
+    }
+
+    static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
+                           jfloat cy, jfloat radius, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawCircle(cx, cy, radius, *paint);
+    }
+
+    static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+            jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
+            jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+        canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
+    }
+
+    static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
+            jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
+            jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+        canvas->drawRoundRect(rect, rx, ry, *paint);
+    }
+
+    static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+                         jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawPath(*path, *paint);
+    }
+
+    static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
+                                          jlong canvasHandle, jlong bitmapHandle,
+                                          jfloat left, jfloat top,
+                                          jlong paintHandle, jint canvasDensity,
+                                          jint screenDensity, jint bitmapDensity) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+        if (canvasDensity == bitmapDensity || canvasDensity == 0
+                || bitmapDensity == 0) {
+            if (screenDensity != 0 && screenDensity != bitmapDensity) {
+                SkPaint filteredPaint;
+                if (paint) {
+                    filteredPaint = *paint;
+                }
+                filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+                canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
+            } else {
+                canvas->drawBitmap(*bitmap, left, top, paint);
+            }
+        } else {
+            canvas->save();
+            SkScalar scale = canvasDensity / (float)bitmapDensity;
+            canvas->translate(left, top);
+            canvas->scale(scale, scale);
+
+            SkPaint filteredPaint;
+            if (paint) {
+                filteredPaint = *paint;
+            }
+            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+
+            canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
+
+            canvas->restore();
+        }
+    }
+
+    static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
+                        jobject srcIRect, const SkRect& dst, SkPaint* paint,
+                        jint screenDensity, jint bitmapDensity) {
+        SkIRect    src, *srcPtr = NULL;
+
+        if (NULL != srcIRect) {
+            GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
+            srcPtr = &src;
+        }
+
+        if (screenDensity != 0 && screenDensity != bitmapDensity) {
+            SkPaint filteredPaint;
+            if (paint) {
+                filteredPaint = *paint;
+            }
+            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
+        } else {
+            canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
+        }
+    }
+
+    static void drawBitmapRF(JNIEnv* env, jobject, jlong canvasHandle,
+                             jlong bitmapHandle, jobject srcIRect,
+                             jobject dstRectF, jlong paintHandle,
+                             jint screenDensity, jint bitmapDensity) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkRect      dst;
+        GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
+        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
+                screenDensity, bitmapDensity);
+    }
+
+    static void drawBitmapRR(JNIEnv* env, jobject, jlong canvasHandle,
+                             jlong bitmapHandle, jobject srcIRect,
+                             jobject dstRect, jlong paintHandle,
+                             jint screenDensity, jint bitmapDensity) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkRect      dst;
+        GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
+        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
+                screenDensity, bitmapDensity);
+    }
+
+    static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
+                                jintArray jcolors, jint offset, jint stride,
+                                jfloat x, jfloat y, jint width, jint height,
+                                jboolean hasAlpha, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+        // correct the alphaType to kOpaque_SkAlphaType.
+        SkImageInfo info = SkImageInfo::Make(width, height,
+                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                               kPremul_SkAlphaType);
+        SkBitmap    bitmap;
+        if (!bitmap.allocPixels(info)) {
+            return;
+        }
+
+        if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
+                0, 0, width, height, bitmap)) {
+            return;
+        }
+
+        canvas->drawBitmap(bitmap, x, y, paint);
+    }
+
+    static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
+                                 jlong bitmapHandle, jlong matrixHandle,
+                                 jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
+    }
+
+    static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle,
+                          jlong bitmapHandle, jint meshWidth, jint meshHeight,
+                          jfloatArray jverts, jint vertIndex, jintArray jcolors,
+                          jint colorIndex, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+        const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+        const int indexCount = meshWidth * meshHeight * 6;
+
+        AutoJavaFloatArray  vertA(env, jverts, vertIndex + (ptCount << 1));
+        AutoJavaIntArray    colorA(env, jcolors, colorIndex + ptCount);
+
+        /*  Our temp storage holds 2 or 3 arrays.
+            texture points [ptCount * sizeof(SkPoint)]
+            optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
+                copy to convert from float to fixed
+            indices [ptCount * sizeof(uint16_t)]
+        */
+        ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
+        storageSize += indexCount * sizeof(uint16_t);  // indices[]
+
+        SkAutoMalloc storage(storageSize);
+        SkPoint* texs = (SkPoint*)storage.get();
+        SkPoint* verts;
+        uint16_t* indices;
+#ifdef SK_SCALAR_IS_FLOAT
+        verts = (SkPoint*)(vertA.ptr() + vertIndex);
+        indices = (uint16_t*)(texs + ptCount);
+#else
+        SkASSERT(false);
+#endif
+
+        // cons up texture coordinates and indices
+        {
+            const SkScalar w = SkIntToScalar(bitmap->width());
+            const SkScalar h = SkIntToScalar(bitmap->height());
+            const SkScalar dx = w / meshWidth;
+            const SkScalar dy = h / meshHeight;
+
+            SkPoint* texsPtr = texs;
+            SkScalar y = 0;
+            for (int i = 0; i <= meshHeight; i++) {
+                if (i == meshHeight) {
+                    y = h;  // to ensure numerically we hit h exactly
+                }
+                SkScalar x = 0;
+                for (int j = 0; j < meshWidth; j++) {
+                    texsPtr->set(x, y);
+                    texsPtr += 1;
+                    x += dx;
+                }
+                texsPtr->set(w, y);
+                texsPtr += 1;
+                y += dy;
+            }
+            SkASSERT(texsPtr - texs == ptCount);
+        }
+
+        // cons up indices
+        {
+            uint16_t* indexPtr = indices;
+            int index = 0;
+            for (int i = 0; i < meshHeight; i++) {
+                for (int j = 0; j < meshWidth; j++) {
+                    // lower-left triangle
+                    *indexPtr++ = index;
+                    *indexPtr++ = index + meshWidth + 1;
+                    *indexPtr++ = index + meshWidth + 2;
+                    // upper-right triangle
+                    *indexPtr++ = index;
+                    *indexPtr++ = index + meshWidth + 2;
+                    *indexPtr++ = index + 1;
+                    // bump to the next cell
+                    index += 1;
+                }
+                // bump to the next row
+                index += 1;
+            }
+            SkASSERT(indexPtr - indices == indexCount);
+            SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
+        }
+
+        // double-check that we have legal indices
+#ifdef SK_DEBUG
+        {
+            for (int i = 0; i < indexCount; i++) {
+                SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+            }
+        }
+#endif
+
+        // cons-up a shader for the bitmap
+        SkPaint tmpPaint;
+        if (paint) {
+            tmpPaint = *paint;
+        }
+        SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
+                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+        SkSafeUnref(tmpPaint.setShader(shader));
+
+        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
+                             texs, (const SkColor*)colorA.ptr(), NULL, indices,
+                             indexCount, tmpPaint);
+    }
+
+    static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
+                             jint modeHandle, jint vertexCount,
+                             jfloatArray jverts, jint vertIndex,
+                             jfloatArray jtexs, jint texIndex,
+                             jintArray jcolors, jint colorIndex,
+                             jshortArray jindices, jint indexIndex,
+                             jint indexCount, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
+        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+        AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
+        AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
+        AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
+        AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
+
+        const int ptCount = vertexCount >> 1;
+
+        SkPoint* verts;
+        SkPoint* texs = NULL;
+#ifdef SK_SCALAR_IS_FLOAT
+        verts = (SkPoint*)(vertA.ptr() + vertIndex);
+        if (jtexs != NULL) {
+            texs = (SkPoint*)(texA.ptr() + texIndex);
+        }
+#else
+        SkASSERT(false);
+#endif
+
+        const SkColor* colors = NULL;
+        const uint16_t* indices = NULL;
+        if (jcolors != NULL) {
+            colors = (const SkColor*)(colorA.ptr() + colorIndex);
+        }
+        if (jindices != NULL) {
+            indices = (const uint16_t*)(indexA.ptr() + indexIndex);
+        }
+
+        canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
+                             indices, indexCount, *paint);
+    }
+
+
+    static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle,
+                                               jcharArray text, jint index, jint count,
+                                               jfloat x, jfloat y, jint bidiFlags,
+                                               jlong paintHandle, jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+    }
+
+    static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject,
+                                                   jlong canvasHandle, jstring text,
+                                                   jint start, jint end,
+                                                   jfloat x, jfloat y, jint bidiFlags,
+                                                   jlong paintHandle, jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        const jchar* textArray = env->GetStringChars(text, NULL);
+        drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface);
+        env->ReleaseStringChars(text, textArray);
+    }
+
+    class DrawTextFunctor {
+    public:
+        DrawTextFunctor(const Layout& layout, SkCanvas* canvas, jfloat x, jfloat y, SkPaint* paint,
+                    uint16_t* glyphs, SkPoint* pos)
+                : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
+                    pos(pos) { }
+
+        void operator()(size_t start, size_t end) {
+            for (size_t i = start; i < end; i++) {
+                glyphs[i] = layout.getGlyphId(i);
+                pos[i].fX = x + layout.getX(i);
+                pos[i].fY = y + layout.getY(i);
+            }
+            canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
+        }
+    private:
+        const Layout& layout;
+        SkCanvas* canvas;
+        jfloat x;
+        jfloat y;
+        SkPaint* paint;
+        uint16_t* glyphs;
+        SkPoint* pos;
+    };
+
+    static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, const Layout& layout, float x, float y) {
+        size_t nGlyphs = layout.nGlyphs();
+        uint16_t* glyphs = new uint16_t[nGlyphs];
+        SkPoint* pos = new SkPoint[nGlyphs];
+
+        x += MinikinUtils::xOffsetForTextAlign(paint, layout);
+        SkPaint::Align align = paint->getTextAlign();
+        paint->setTextAlign(SkPaint::kLeft_Align);
+        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        DrawTextFunctor f(layout, canvas, x, y, paint, glyphs, pos);
+        MinikinUtils::forFontRun(layout, paint, f);
+        doDrawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
+        paint->setTextAlign(align);
+        delete[] glyphs;
+        delete[] pos;
+    }
+
+    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+            int start, int end,
+            jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
+
+        jint count = end - start;
+        drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint,
+                typeface);
+    }
+
+    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+            int start, int count, int contextCount,
+            jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
+
+        Layout layout;
+        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+        layout.doLayout(textArray, start, count, contextCount, css);
+        drawGlyphsToSkia(canvas, paint, layout, x, y);
+    }
+
+// Same values used by Skia
+#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
+#define kStdUnderline_Offset    (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+    static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length,
+            SkPaint* paint) {
+        uint32_t flags;
+        SkDrawFilter* drawFilter = canvas->getDrawFilter();
+        if (drawFilter) {
+            SkPaint paintCopy(*paint);
+            drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+            flags = paintCopy.getFlags();
+        } else {
+            flags = paint->getFlags();
+        }
+        if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+            SkScalar left = x;
+            SkScalar right = x + length;
+            float textSize = paint->getTextSize();
+            float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+            if (flags & SkPaint::kUnderlineText_Flag) {
+                SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+                SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+                canvas->drawRectCoords(left, top, right, bottom, *paint);
+            }
+            if (flags & SkPaint::kStrikeThruText_Flag) {
+                SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+                SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+                canvas->drawRectCoords(left, top, right, bottom, *paint);
+            }
+        }
+    }
+
+    static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
+            int index, int count, jfloat x, jfloat y, SkPaint* paint) {
+        SkPoint* posPtr = new SkPoint[count];
+        for (int indx = 0; indx < count; indx++) {
+            posPtr[indx].fX = x + posArray[indx * 2];
+            posPtr[indx].fY = y + posArray[indx * 2 + 1];
+        }
+        canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
+        delete[] posPtr;
+    }
+
+    static void drawTextRun___CIIIIFFZPaintTypeface(
+            JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+            jint count, jint contextIndex, jint contextCount,
+            jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+        jchar* chars = env->GetCharArrayElements(text, NULL);
+        drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
+                count, contextCount, x, y, bidiFlags, paint, typeface);
+        env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
+    }
+
+    static void drawTextRun__StringIIIIFFZPaintTypeface(
+            JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
+            jint end, jint contextStart, jint contextEnd,
+            jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+        jint count = end - start;
+        jint contextCount = contextEnd - contextStart;
+        const jchar* chars = env->GetStringChars(text, NULL);
+        drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
+                count, contextCount, x, y, bidiFlags, paint, typeface);
+        env->ReleaseStringChars(text, chars);
+    }
+
+    static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
+                                         jcharArray text, jint index, jint count,
+                                         jfloatArray pos, jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
+        jsize textCount = text ? env->GetArrayLength(text) : NULL;
+        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+        int indx;
+        for (indx = 0; indx < posCount; indx++) {
+            posPtr[indx].fX = posArray[indx << 1];
+            posPtr[indx].fY = posArray[(indx << 1) + 1];
+        }
+
+        SkPaint::TextEncoding encoding = paint->getTextEncoding();
+        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+        canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
+        paint->setTextEncoding(encoding);
+
+        if (text) {
+            env->ReleaseCharArrayElements(text, textArray, 0);
+        }
+        if (pos) {
+            env->ReleaseFloatArrayElements(pos, posArray, 0);
+        }
+        delete[] posPtr;
+    }
+
+    static void drawPosText__String_FPaint(JNIEnv* env, jobject,
+                                           jlong canvasHandle, jstring text,
+                                           jfloatArray pos,
+                                           jlong paintHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
+        int byteLength = text ? env->GetStringLength(text) : 0;
+        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+
+        for (int indx = 0; indx < posCount; indx++) {
+            posPtr[indx].fX = posArray[indx << 1];
+            posPtr[indx].fY = posArray[(indx << 1) + 1];
+        }
+
+        SkPaint::TextEncoding encoding = paint->getTextEncoding();
+        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+        canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
+        paint->setTextEncoding(encoding);
+
+        if (text) {
+            env->ReleaseStringChars(text, (const jchar*) text_);
+        }
+        if (pos) {
+            env->ReleaseFloatArrayElements(pos, posArray, 0);
+        }
+        delete[] posPtr;
+    }
+
+    class DrawTextOnPathFunctor {
+    public:
+        DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
+                    float vOffset, SkPaint* paint, SkPath* path)
+                : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+                    paint(paint), path(path) {
+        }
+        void operator()(size_t start, size_t end) {
+            uint16_t glyphs[1];
+            for (size_t i = start; i < end; i++) {
+                glyphs[0] = layout.getGlyphId(i);
+                float x = hOffset + layout.getX(i);
+                float y = vOffset + layout.getY(i);
+                canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
+            }
+        }
+    private:
+        const Layout& layout;
+        SkCanvas* canvas;
+        float hOffset;
+        float vOffset;
+        SkPaint* paint;
+        SkPath* path;
+    };
+
+    static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
+            float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
+        Layout layout;
+        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+        layout.doLayout(text, 0, count, count, css);
+        hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
+        // Set align to left for drawing, as we don't want individual
+        // glyphs centered or right-aligned; the offset above takes
+        // care of all alignment.
+        SkPaint::Align align = paint->getTextAlign();
+        paint->setTextAlign(SkPaint::kLeft_Align);
+
+        DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
+        MinikinUtils::forFontRun(layout, paint, f);
+        paint->setTextAlign(align);
+    }
+
+    static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
+            jlong canvasHandle, jcharArray text, jint index, jint count,
+            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+            jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas, typeface);
+        env->ReleaseCharArrayElements(text, textArray, 0);
+    }
+
+    static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
+            jlong canvasHandle, jstring text, jlong pathHandle,
+            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+            jlong typefaceHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+        const jchar* text_ = env->GetStringChars(text, NULL);
+        int count = env->GetStringLength(text);
+        doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas, typeface);
+        env->ReleaseStringChars(text, text_);
+    }
+
+
+    // This function is a mirror of SkCanvas::getClipBounds except that it does
+    // not outset the edge of the clip to account for anti-aliasing. There is
+    // a skia bug to investigate pushing this logic into back into skia.
+    // (see https://code.google.com/p/skia/issues/detail?id=1303)
+    static bool getHardClipBounds(SkCanvas* canvas, SkRect* bounds) {
+        SkIRect ibounds;
+        if (!canvas->getClipDeviceBounds(&ibounds)) {
+            return false;
+        }
+
+        SkMatrix inverse;
+        // if we can't invert the CTM, we can't return local clip bounds
+        if (!canvas->getTotalMatrix().invert(&inverse)) {
+            if (bounds) {
+                bounds->setEmpty();
+            }
+            return false;
+        }
+
+        if (NULL != bounds) {
+            SkRect r = SkRect::Make(ibounds);
+            inverse.mapRect(bounds, r);
+        }
+        return true;
+    }
+
+    static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
+                                  jobject bounds) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkRect   r;
+        SkIRect ir;
+        bool result = getHardClipBounds(canvas, &r);
+
+        if (!result) {
+            r.setEmpty();
+        }
+        r.round(&ir);
+
+        (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
+                       jlong matrixHandle) {
+        SkCanvas* canvas = getNativeCanvas(canvasHandle);
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        *matrix = canvas->getTotalMatrix();
+    }
+};
+
+static JNINativeMethod gCanvasMethods[] = {
+    {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
+    {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
+    {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
+    {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
+    {"native_isOpaque","(J)Z", (void*) SkCanvasGlue::isOpaque},
+    {"native_getWidth","(J)I", (void*) SkCanvasGlue::getWidth},
+    {"native_getHeight","(J)I", (void*) SkCanvasGlue::getHeight},
+    {"native_save","(JI)I", (void*) SkCanvasGlue::save},
+    {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer},
+    {"native_saveLayerAlpha","(JFFFFII)I", (void*) SkCanvasGlue::saveLayerAlpha},
+    {"native_restore","(J)V", (void*) SkCanvasGlue::restore},
+    {"native_getSaveCount","(J)I", (void*) SkCanvasGlue::getSaveCount},
+    {"native_restoreToCount","(JI)V", (void*) SkCanvasGlue::restoreToCount},
+    {"native_translate","(JFF)V", (void*) SkCanvasGlue::translate},
+    {"native_scale","(JFF)V", (void*) SkCanvasGlue::scale__FF},
+    {"native_rotate","(JF)V", (void*) SkCanvasGlue::rotate__F},
+    {"native_skew","(JFF)V", (void*) SkCanvasGlue::skew__FF},
+    {"native_concat","(JJ)V", (void*) SkCanvasGlue::concat},
+    {"native_setMatrix","(JJ)V", (void*) SkCanvasGlue::setMatrix},
+    {"native_clipRect","(JFFFFI)Z", (void*) SkCanvasGlue::clipRect},
+    {"native_clipPath","(JJI)Z", (void*) SkCanvasGlue::clipPath},
+    {"native_clipRegion","(JJI)Z", (void*) SkCanvasGlue::clipRegion},
+    {"nativeSetDrawFilter", "(JJ)V", (void*) SkCanvasGlue::setDrawFilter},
+    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z",
+        (void*) SkCanvasGlue::getClipBounds},
+    {"native_getCTM", "(JJ)V", (void*)SkCanvasGlue::getCTM},
+    {"native_quickReject","(JJ)Z", (void*) SkCanvasGlue::quickReject__Path},
+    {"native_quickReject","(JFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
+    {"native_drawRGB","(JIII)V", (void*) SkCanvasGlue::drawRGB},
+    {"native_drawARGB","(JIIII)V", (void*) SkCanvasGlue::drawARGB},
+    {"native_drawColor","(JI)V", (void*) SkCanvasGlue::drawColor__I},
+    {"native_drawColor","(JII)V", (void*) SkCanvasGlue::drawColor__II},
+    {"native_drawPaint","(JJ)V", (void*) SkCanvasGlue::drawPaint},
+    {"native_drawPoint", "(JFFJ)V", (void*) SkCanvasGlue::drawPoint},
+    {"native_drawPoints", "(J[FIIJ)V", (void*) SkCanvasGlue::drawPoints},
+    {"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
+    {"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
+    {"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
+    {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
+    {"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
+    {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
+    {"native_drawRoundRect","(JFFFFFFJ)V",
+        (void*) SkCanvasGlue::drawRoundRect},
+    {"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
+    {"native_drawBitmap","(JJFFJIII)V",
+        (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
+    {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/RectF;JII)V",
+        (void*) SkCanvasGlue::drawBitmapRF},
+    {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/Rect;JII)V",
+        (void*) SkCanvasGlue::drawBitmapRR},
+    {"native_drawBitmap", "(J[IIIFFIIZJ)V",
+    (void*)SkCanvasGlue::drawBitmapArray},
+    {"nativeDrawBitmapMatrix", "(JJJJ)V",
+        (void*)SkCanvasGlue::drawBitmapMatrix},
+    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V",
+        (void*)SkCanvasGlue::drawBitmapMesh},
+    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V",
+        (void*)SkCanvasGlue::drawVertices},
+    {"native_drawText","(J[CIIFFIJJ)V",
+        (void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface},
+    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V",
+        (void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface},
+    {"native_drawTextRun","(J[CIIIIFFZJJ)V",
+        (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
+    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
+        (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
+    {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
+        (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
+    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
+        (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
+
+    {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
+
+    {"freeTextLayoutCaches", "()V", (void*) SkCanvasGlue::freeTextLayoutCaches}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+#define REG(env, name, array) \
+    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
+                                                    SK_ARRAY_COUNT(array));  \
+    if (result < 0) return result
+
+int register_android_graphics_Canvas(JNIEnv* env) {
+    int result;
+
+    REG(env, "android/graphics/Canvas", gCanvasMethods);
+
+    return result;
+}
+
+} // namespace android
+
+// GraphicsJNI helper for external clients.
+// We keep the implementation here to avoid exposing NativeCanvasWrapper
+// externally.
+SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
+    return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
+}
diff --git a/core/jni/android/graphics/Canvas.h b/core/jni/android/graphics/Canvas.h
deleted file mode 100644
index 710845d..0000000
--- a/core/jni/android/graphics/Canvas.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GRAPHICS_CANVAS_H
-#define ANDROID_GRAPHICS_CANVAS_H
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkMatrix.h"
-
-namespace android {
-
-// TODO: move this further up the stack so that all interaction with minikin
-//       happens prior to calling into this interface
-class TypefaceImpl;
-
-class Canvas {
-public:
-    virtual ~Canvas() {};
-
-    static Canvas* create_canvas(SkBitmap* bitmap);
-    static Canvas* create_canvas(SkCanvas* skiaCanvas);
-
-    // TODO: enable HWUI to either create similar canvas wrapper or subclass
-    //       directly from Canvas
-    //static Canvas* create_canvas(uirenderer::Renderer* renderer);
-
-    // TODO: this is a temporary affordance until all necessary logic can be
-    //       moved within this interface! Further, the return value should
-    //       NOT be unref'd and is valid until this canvas is destroyed or a
-    //       new bitmap is set.
-    virtual SkCanvas* getSkCanvas() = 0;
-
-    virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
-
-    virtual bool isOpaque() = 0;
-    virtual int width() = 0;
-    virtual int height() = 0;
-
-// ----------------------------------------------------------------------------
-// Canvas state operations
-// ----------------------------------------------------------------------------
-    // Save (layer)
-    virtual int getSaveCount() const = 0;
-    virtual int save(SkCanvas::SaveFlags flags) = 0;
-    virtual void restore() = 0;
-    virtual void restoreToCount(int saveCount) = 0;
-
-    virtual int saveLayer(float left, float top, float right, float bottom,
-                const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
-    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags) = 0;
-
-    // Matrix
-    virtual void getMatrix(SkMatrix* outMatrix) const = 0;
-    virtual void setMatrix(const SkMatrix& matrix) = 0;
-
-    virtual void concat(const SkMatrix& matrix) = 0;
-    virtual void rotate(float degrees) = 0;
-    virtual void scale(float sx, float sy) = 0;
-    virtual void skew(float sx, float sy) = 0;
-    virtual void translate(float dx, float dy) = 0;
-
-    // clip
-    virtual bool getClipBounds(SkRect* outRect) const = 0;
-    virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
-    virtual bool quickRejectPath(const SkPath& path) const = 0;
-
-    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
-    virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
-    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
-
-    // filters
-    virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations
-// ----------------------------------------------------------------------------
-    virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
-    virtual void drawPaint(const SkPaint& paint) = 0;
-
-    // Geometry
-    virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
-    virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
-    virtual void drawLine(float startX, float startY, float stopX, float stopY,
-                const SkPaint& paint) = 0;
-    virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
-    virtual void drawRect(float left, float top, float right, float bottom,
-            const SkPaint& paint) = 0;
-    virtual void drawRoundRect(float left, float top, float right, float bottom,
-            float rx, float ry, const SkPaint& paint) = 0;
-    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
-    virtual void drawOval(float left, float top, float right, float bottom,
-            const SkPaint& paint) = 0;
-    virtual void drawArc(float left, float top, float right, float bottom,
-            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
-    virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
-    virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
-                              const float* verts, const float* tex, const int* colors,
-                              const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
-
-    // Bitmap-based
-    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
-            const SkPaint* paint) = 0;
-    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
-            const SkPaint* paint) = 0;
-    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
-            float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, const SkPaint* paint) = 0;
-    virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
-            const float* vertices, const int* colors, const SkPaint* paint) = 0;
-
-    // Text
-    virtual void drawText(const char* text, int start, int count, int contextCount,
-            float x, float y, int bidiFlags, const SkPaint& paint,
-            TypefaceImpl* typeface) = 0;
-    virtual void drawPosText(const char* text, const float* positions, int count, int posCount,
-            const SkPaint& paint) = 0;
-    virtual void drawTextOnPath(const char* text, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) = 0;
-};
-
-}; // namespace android
-#endif // ANDROID_GRAPHICS_CANVAS_H
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 74be577..5cc2b95 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -4,7 +4,6 @@
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
 
-#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkMath.h"
@@ -365,7 +364,7 @@
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
     jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
-    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+    SkCanvas* c = getNativeCanvas(canvasHandle);
     SkASSERT(c);
     return c;
 }
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 28a6edb..8150edf 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -47,6 +47,7 @@
     static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
     static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
 
+    static SkCanvas* getNativeCanvas(jlong nativeHandle);
     static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
     static SkPaint*  getNativePaint(JNIEnv*, jobject paint);
     static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index e82e8a6..ab5bdb0 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,7 +23,6 @@
 
 #include <Caches.h>
 
-#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
@@ -120,7 +119,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
@@ -139,7 +138,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index d214575..bc0c25f 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include "Canvas.h"
 #include "Picture.h"
 
+#include "SkCanvas.h"
 #include "SkStream.h"
 
 namespace android {
@@ -36,13 +36,12 @@
     }
 }
 
-Canvas* Picture::beginRecording(int width, int height) {
+SkCanvas* Picture::beginRecording(int width, int height) {
     mPicture.reset(NULL);
     mRecorder.reset(new SkPictureRecorder);
     mWidth = width;
     mHeight = height;
-    SkCanvas* canvas = mRecorder->beginRecording(width, height, NULL, 0);
-    return Canvas::create_canvas(canvas);
+    return mRecorder->beginRecording(width, height, NULL, 0);
 }
 
 void Picture::endRecording() {
@@ -94,14 +93,14 @@
     }
 }
 
-void Picture::draw(Canvas* canvas) {
+void Picture::draw(SkCanvas* canvas) {
     if (NULL != mRecorder.get()) {
         this->endRecording();
         SkASSERT(NULL != mPicture.get());
     }
     if (NULL != mPicture.get()) {
         // TODO: remove this const_cast once pictures are immutable
-        const_cast<SkPicture*>(mPicture.get())->draw(canvas->getSkCanvas());
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
     }
 }
 
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index a2e5d4a..abb0403 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -22,13 +22,14 @@
 #include "SkRefCnt.h"
 #include "SkTemplates.h"
 
+class SkCanvas;
+class SkPicture;
+class SkPictureRecorder;
 class SkStream;
 class SkWStream;
 
 namespace android {
 
-class Canvas;
-
 // Skia's SkPicture class has been split into an SkPictureRecorder
 // and an SkPicture. AndroidPicture recreates the functionality
 // of the old SkPicture interface by flip-flopping between the two
@@ -37,7 +38,7 @@
 public:
     explicit Picture(const Picture* src = NULL);
 
-    Canvas* beginRecording(int width, int height);
+    SkCanvas* beginRecording(int width, int height);
 
     void endRecording();
 
@@ -49,7 +50,7 @@
 
     void serialize(SkWStream* stream) const;
 
-    void draw(Canvas* canvas);
+    void draw(SkCanvas* canvas);
 
 private:
     int mWidth;
diff --git a/core/jni/android/graphics/SkiaCanvas.cpp b/core/jni/android/graphics/SkiaCanvas.cpp
deleted file mode 100644
index 5e93313..0000000
--- a/core/jni/android/graphics/SkiaCanvas.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni.h"
-#include "Canvas.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "SkCanvas.h"
-#include "SkClipStack.h"
-#include "SkDevice.h"
-#include "SkDeque.h"
-#include "SkDrawFilter.h"
-#include "SkGraphics.h"
-#include "SkPorterDuff.h"
-#include "SkShader.h"
-#include "SkTArray.h"
-#include "SkTemplates.h"
-
-#include <minikin/Layout.h>
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-
-#include "TypefaceImpl.h"
-
-#include "unicode/ubidi.h"
-#include "unicode/ushape.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-// Holds an SkCanvas reference plus additional native data.
-class SkiaCanvas : public Canvas {
-public:
-    SkiaCanvas(SkBitmap* bitmap);
-
-    SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
-        SkASSERT(canvas);
-    }
-
-    virtual SkCanvas* getSkCanvas() {
-        return mCanvas.get();
-    }
-
-    virtual void setBitmap(SkBitmap* bitmap, bool copyState);
-
-    virtual bool isOpaque();
-    virtual int width();
-    virtual int height();
-
-    virtual int getSaveCount() const;
-    virtual int save(SkCanvas::SaveFlags flags);
-    virtual void restore();
-    virtual void restoreToCount(int saveCount);
-
-    virtual int saveLayer(float left, float top, float right, float bottom,
-                const SkPaint* paint, SkCanvas::SaveFlags flags);
-    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags);
-
-    virtual void getMatrix(SkMatrix* outMatrix) const;
-    virtual void setMatrix(const SkMatrix& matrix);
-    virtual void concat(const SkMatrix& matrix);
-    virtual void rotate(float degrees);
-    virtual void scale(float sx, float sy);
-    virtual void skew(float sx, float sy);
-    virtual void translate(float dx, float dy);
-
-    virtual bool getClipBounds(SkRect* outRect) const;
-    virtual bool quickRejectRect(float left, float top, float right, float bottom) const;
-    virtual bool quickRejectPath(const SkPath& path) const;
-    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
-    virtual bool clipPath(const SkPath* path, SkRegion::Op op);
-    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
-
-    virtual void setDrawFilter(SkDrawFilter* drawFilter);
-
-    virtual void drawColor(int color, SkXfermode::Mode mode);
-    virtual void drawPaint(const SkPaint& paint);
-
-    virtual void drawPoint(float x, float y, const SkPaint& paint);
-    virtual void drawPoints(const float* points, int count, const SkPaint& paint);
-    virtual void drawLine(float startX, float startY, float stopX, float stopY,
-            const SkPaint& paint);
-    virtual void drawLines(const float* points, int count, const SkPaint& paint);
-    virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint);
-    virtual void drawRoundRect(float left, float top, float right, float bottom,
-            float rx, float ry, const SkPaint& paint);
-    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint);
-    virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint);
-    virtual void drawArc(float left, float top, float right, float bottom,
-            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint);
-    virtual void drawPath(const SkPath& path, const SkPaint& paint);
-    virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
-            const float* verts, const float* tex, const int* colors,
-            const uint16_t* indices, int indexCount, const SkPaint& paint);
-
-    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint);
-    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
-    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
-            float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, const SkPaint* paint);
-    virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
-            const float* vertices, const int* colors, const SkPaint* paint);
-
-    virtual void drawText(const char* text, int start, int count, int contextCount,
-            float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface);
-    virtual void drawPosText(const char* text, const float* positions, int count, int posCount,
-            const SkPaint& paint);
-    virtual void drawTextOnPath(const char* text, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint);
-
-private:
-    struct SaveRec {
-        int                 saveCount;
-        SkCanvas::SaveFlags saveFlags;
-    };
-
-    void recordPartialSave(SkCanvas::SaveFlags flags);
-    void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
-    void applyClips(const SkTArray<SkClipStack::Element>& clips);
-
-    void drawPoints(const float* points, int count, const SkPaint& paint,
-                    SkCanvas::PointMode mode);
-    void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
-
-    SkAutoTUnref<SkCanvas> mCanvas;
-    SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
-};
-
-// Construct an SkCanvas from the bitmap.
-static SkCanvas* createCanvas(SkBitmap* bitmap) {
-    if (bitmap) {
-        return SkNEW_ARGS(SkCanvas, (*bitmap));
-    }
-
-    // Create an empty bitmap device to prevent callers from crashing
-    // if they attempt to draw into this canvas.
-    SkBitmap emptyBitmap;
-    return new SkCanvas(emptyBitmap);
-}
-
-Canvas* Canvas::create_canvas(SkBitmap* bitmap) {
-    return new SkiaCanvas(bitmap);
-}
-
-Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
-    return new SkiaCanvas(skiaCanvas);
-}
-
-SkiaCanvas::SkiaCanvas(SkBitmap* bitmap) {
-    mCanvas.reset(createCanvas(bitmap));
-}
-
-// ----------------------------------------------------------------------------
-// Canvas state operations: Replace Bitmap
-// ----------------------------------------------------------------------------
-
-class ClipCopier : public SkCanvas::ClipVisitor {
-public:
-    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
-
-    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipRect(rect, op, antialias);
-    }
-    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipRRect(rrect, op, antialias);
-    }
-    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipPath(path, op, antialias);
-    }
-
-private:
-    SkCanvas* m_dstCanvas;
-};
-
-void SkiaCanvas::setBitmap(SkBitmap* bitmap, bool copyState) {
-    SkCanvas* newCanvas = createCanvas(bitmap);
-    SkASSERT(newCanvas);
-
-    if (copyState) {
-        // Copy the canvas matrix & clip state.
-        newCanvas->setMatrix(mCanvas->getTotalMatrix());
-        if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
-            ClipCopier copier(newCanvas);
-            mCanvas->replayClips(&copier);
-        }
-    }
-
-    // unrefs the existing canvas
-    mCanvas.reset(newCanvas);
-
-    // clean up the old save stack
-    mSaveStack.reset(NULL);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas state operations
-// ----------------------------------------------------------------------------
-
-bool SkiaCanvas::isOpaque() {
-    return mCanvas->getDevice()->accessBitmap(false).isOpaque();
-}
-
-int SkiaCanvas::width() {
-    return mCanvas->getBaseLayerSize().width();
-}
-
-int SkiaCanvas::height() {
-    return mCanvas->getBaseLayerSize().height();
-}
-
-// ----------------------------------------------------------------------------
-// Canvas state operations: Save (layer)
-// ----------------------------------------------------------------------------
-
-int SkiaCanvas::getSaveCount() const {
-    return mCanvas->getSaveCount();
-}
-
-int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
-    int count = mCanvas->save();
-    recordPartialSave(flags);
-    return count;
-}
-
-void SkiaCanvas::restore() {
-    const SaveRec* rec = (NULL == mSaveStack.get())
-            ? NULL
-            : static_cast<SaveRec*>(mSaveStack->back());
-    int currentSaveCount = mCanvas->getSaveCount() - 1;
-    SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
-
-    if (NULL == rec || rec->saveCount != currentSaveCount) {
-        // Fast path - no record for this frame.
-        mCanvas->restore();
-        return;
-    }
-
-    bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
-    bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
-
-    SkMatrix savedMatrix;
-    if (preserveMatrix) {
-        savedMatrix = mCanvas->getTotalMatrix();
-    }
-
-    SkTArray<SkClipStack::Element> savedClips;
-    if (preserveClip) {
-        saveClipsForFrame(savedClips, currentSaveCount);
-    }
-
-    mCanvas->restore();
-
-    if (preserveMatrix) {
-        mCanvas->setMatrix(savedMatrix);
-    }
-
-    if (preserveClip && !savedClips.empty()) {
-        applyClips(savedClips);
-    }
-
-    mSaveStack->pop_back();
-}
-
-void SkiaCanvas::restoreToCount(int restoreCount) {
-    while (mCanvas->getSaveCount() > restoreCount) {
-        this->restore();
-    }
-}
-
-int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
-            const SkPaint* paint, SkCanvas::SaveFlags flags) {
-    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
-    recordPartialSave(flags);
-    return count;
-}
-
-int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
-        int alpha, SkCanvas::SaveFlags flags) {
-    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
-    recordPartialSave(flags);
-    return count;
-}
-
-// ----------------------------------------------------------------------------
-// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
-// ----------------------------------------------------------------------------
-
-void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
-    // A partial save is a save operation which doesn't capture the full canvas state.
-    // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
-
-    // Mask-out non canvas state bits.
-    flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
-
-    if (SkCanvas::kMatrixClip_SaveFlag == flags) {
-        // not a partial save.
-        return;
-    }
-
-    if (NULL == mSaveStack.get()) {
-        mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
-    }
-
-    SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
-    // Store the save counter in the SkClipStack domain.
-    // (0-based, equal to the number of save ops on the stack).
-    rec->saveCount = mCanvas->getSaveCount() - 1;
-    rec->saveFlags = flags;
-}
-
-void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
-    SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
-                                   SkClipStack::Iter::kTop_IterStart);
-    while (const SkClipStack::Element* elem = clipIterator.next()) {
-        if (elem->getSaveCount() < frameSaveCount) {
-            // done with the current frame.
-            break;
-        }
-        SkASSERT(elem->getSaveCount() == frameSaveCount);
-        clips.push_back(*elem);
-    }
-}
-
-void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
-    ClipCopier clipCopier(mCanvas);
-
-    // The clip stack stores clips in device space.
-    SkMatrix origMatrix = mCanvas->getTotalMatrix();
-    mCanvas->resetMatrix();
-
-    // We pushed the clips in reverse order.
-    for (int i = clips.count() - 1; i >= 0; --i) {
-        clips[i].replay(&clipCopier);
-    }
-
-    mCanvas->setMatrix(origMatrix);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas state operations: Matrix
-// ----------------------------------------------------------------------------
-
-void SkiaCanvas::getMatrix(SkMatrix* outMatrix) const {
-    *outMatrix = mCanvas->getTotalMatrix();
-}
-
-void SkiaCanvas::setMatrix(const SkMatrix& matrix) {
-    mCanvas->setMatrix(matrix);
-}
-
-void SkiaCanvas::concat(const SkMatrix& matrix) {
-    mCanvas->concat(matrix);
-}
-
-void SkiaCanvas::rotate(float degrees) {
-    mCanvas->rotate(degrees);
-}
-
-void SkiaCanvas::scale(float sx, float sy) {
-    mCanvas->scale(sx, sy);
-}
-
-void SkiaCanvas::skew(float sx, float sy) {
-    mCanvas->skew(sx, sy);
-}
-
-void SkiaCanvas::translate(float dx, float dy) {
-    mCanvas->translate(dx, dy);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas state operations: Clips
-// ----------------------------------------------------------------------------
-
-// This function is a mirror of SkCanvas::getClipBounds except that it does
-// not outset the edge of the clip to account for anti-aliasing. There is
-// a skia bug to investigate pushing this logic into back into skia.
-// (see https://code.google.com/p/skia/issues/detail?id=1303)
-bool SkiaCanvas::getClipBounds(SkRect* outRect) const {
-    SkIRect ibounds;
-    if (!mCanvas->getClipDeviceBounds(&ibounds)) {
-        return false;
-    }
-
-    SkMatrix inverse;
-    // if we can't invert the CTM, we can't return local clip bounds
-    if (!mCanvas->getTotalMatrix().invert(&inverse)) {
-        if (outRect) {
-            outRect->setEmpty();
-        }
-        return false;
-    }
-
-    if (NULL != outRect) {
-        SkRect r = SkRect::Make(ibounds);
-        inverse.mapRect(outRect, r);
-    }
-    return true;
-}
-
-bool SkiaCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
-    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    return mCanvas->quickReject(bounds);
-}
-
-bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
-    return mCanvas->quickReject(path);
-}
-
-bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
-    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->clipRect(rect, op);
-    return mCanvas->isClipEmpty();
-}
-
-bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
-    mCanvas->clipPath(*path, op);
-    return mCanvas->isClipEmpty();
-}
-
-bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
-    SkPath rgnPath;
-    if (region->getBoundaryPath(&rgnPath)) {
-        // The region is specified in device space.
-        SkMatrix savedMatrix = mCanvas->getTotalMatrix();
-        mCanvas->resetMatrix();
-        mCanvas->clipPath(rgnPath, op);
-        mCanvas->setMatrix(savedMatrix);
-    } else {
-        mCanvas->clipRect(SkRect::MakeEmpty(), op);
-    }
-    return mCanvas->isClipEmpty();
-}
-
-// ----------------------------------------------------------------------------
-// Canvas state operations: Filters
-// ----------------------------------------------------------------------------
-
-void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
-    mCanvas->setDrawFilter(drawFilter);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations
-// ----------------------------------------------------------------------------
-
-void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
-    mCanvas->drawColor(color, mode);
-}
-
-void SkiaCanvas::drawPaint(const SkPaint& paint) {
-    mCanvas->drawPaint(paint);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations: Geometry
-// ----------------------------------------------------------------------------
-
-void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
-                            SkCanvas::PointMode mode) {
-    // convert the floats into SkPoints
-    count >>= 1;    // now it is the number of points
-    SkAutoSTMalloc<32, SkPoint> storage(count);
-    SkPoint* pts = storage.get();
-    for (int i = 0; i < count; i++) {
-        pts[i].set(points[0], points[1]);
-        points += 2;
-    }
-    mCanvas->drawPoints(mode, count, pts, paint);
-}
-
-
-void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
-    mCanvas->drawPoint(x, y, paint);
-}
-
-void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
-    this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
-}
-
-void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
-                          const SkPaint& paint) {
-    mCanvas->drawLine(startX, startY, stopX, stopY, paint);
-}
-
-void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
-    this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
-}
-
-void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
-        const SkPaint& paint) {
-    mCanvas->drawRectCoords(left, top, right, bottom, paint);
-
-}
-
-void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
-        float rx, float ry, const SkPaint& paint) {
-    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawRoundRect(rect, rx, ry, paint);
-}
-
-void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
-    mCanvas->drawCircle(x, y, radius, paint);
-}
-
-void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
-    SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawOval(oval, paint);
-}
-
-void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
-        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
-    SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
-}
-
-void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
-    mCanvas->drawPath(path, paint);
-}
-
-void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
-                              const float* verts, const float* texs, const int* colors,
-                              const uint16_t* indices, int indexCount, const SkPaint& paint) {
-#ifndef SK_SCALAR_IS_FLOAT
-    SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
-#endif
-    const int ptCount = vertexCount >> 1;
-    mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs,
-                          (SkColor*)colors, NULL, indices, indexCount, paint);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations: Bitmaps
-// ----------------------------------------------------------------------------
-
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
-    mCanvas->drawBitmap(bitmap, left, top, paint);
-}
-
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
-    mCanvas->drawBitmapMatrix(bitmap, matrix, paint);
-}
-
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
-                            float srcRight, float srcBottom, float dstLeft, float dstTop,
-                            float dstRight, float dstBottom, const SkPaint* paint) {
-    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
-    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
-    mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
-}
-
-void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
-        const float* vertices, const int* colors, const SkPaint* paint) {
-
-    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
-    const int indexCount = meshWidth * meshHeight * 6;
-
-    /*  Our temp storage holds 2 or 3 arrays.
-        texture points [ptCount * sizeof(SkPoint)]
-        optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
-            copy to convert from float to fixed
-        indices [ptCount * sizeof(uint16_t)]
-    */
-    ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
-    storageSize += indexCount * sizeof(uint16_t);  // indices[]
-
-
-#ifndef SK_SCALAR_IS_FLOAT
-    SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
-#endif
-    SkAutoMalloc storage(storageSize);
-    SkPoint* texs = (SkPoint*)storage.get();
-    uint16_t* indices = (uint16_t*)(texs + ptCount);
-
-    // cons up texture coordinates and indices
-    {
-        const SkScalar w = SkIntToScalar(bitmap.width());
-        const SkScalar h = SkIntToScalar(bitmap.height());
-        const SkScalar dx = w / meshWidth;
-        const SkScalar dy = h / meshHeight;
-
-        SkPoint* texsPtr = texs;
-        SkScalar y = 0;
-        for (int i = 0; i <= meshHeight; i++) {
-            if (i == meshHeight) {
-                y = h;  // to ensure numerically we hit h exactly
-            }
-            SkScalar x = 0;
-            for (int j = 0; j < meshWidth; j++) {
-                texsPtr->set(x, y);
-                texsPtr += 1;
-                x += dx;
-            }
-            texsPtr->set(w, y);
-            texsPtr += 1;
-            y += dy;
-        }
-        SkASSERT(texsPtr - texs == ptCount);
-    }
-
-    // cons up indices
-    {
-        uint16_t* indexPtr = indices;
-        int index = 0;
-        for (int i = 0; i < meshHeight; i++) {
-            for (int j = 0; j < meshWidth; j++) {
-                // lower-left triangle
-                *indexPtr++ = index;
-                *indexPtr++ = index + meshWidth + 1;
-                *indexPtr++ = index + meshWidth + 2;
-                // upper-right triangle
-                *indexPtr++ = index;
-                *indexPtr++ = index + meshWidth + 2;
-                *indexPtr++ = index + 1;
-                // bump to the next cell
-                index += 1;
-            }
-            // bump to the next row
-            index += 1;
-        }
-        SkASSERT(indexPtr - indices == indexCount);
-        SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
-    }
-
-    // double-check that we have legal indices
-#ifdef SK_DEBUG
-    {
-        for (int i = 0; i < indexCount; i++) {
-            SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
-        }
-    }
-#endif
-
-    // cons-up a shader for the bitmap
-    SkPaint tmpPaint;
-    if (paint) {
-        tmpPaint = *paint;
-    }
-    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
-                                                    SkShader::kClamp_TileMode,
-                                                    SkShader::kClamp_TileMode);
-    SkSafeUnref(tmpPaint.setShader(shader));
-
-    mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
-                         texs, (const SkColor*)colors, NULL, indices,
-                         indexCount, tmpPaint);
-}
-
-// ----------------------------------------------------------------------------
-// Canvas draw operations: Text
-// ----------------------------------------------------------------------------
-
-class DrawTextFunctor {
-public:
-    DrawTextFunctor(const Layout& layout, SkCanvas* canvas, float x, float y, SkPaint* paint,
-                uint16_t* glyphs, SkPoint* pos)
-            : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
-                pos(pos) { }
-
-    void operator()(size_t start, size_t end) {
-        for (size_t i = start; i < end; i++) {
-            glyphs[i] = layout.getGlyphId(i);
-            pos[i].fX = x + layout.getX(i);
-            pos[i].fY = y + layout.getY(i);
-        }
-        canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
-    }
-private:
-    const Layout& layout;
-    SkCanvas* canvas;
-    float x;
-    float y;
-    SkPaint* paint;
-    uint16_t* glyphs;
-    SkPoint* pos;
-};
-
-void SkiaCanvas::drawText(const char* text, int start, int count, int contextCount,
-        float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface) {
-    Layout layout;
-    std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
-    layout.doLayout((uint16_t*)text, start, count, contextCount, css);
-
-    size_t nGlyphs = layout.nGlyphs();
-    uint16_t* glyphs = new uint16_t[nGlyphs];
-    SkPoint* pos = new SkPoint[nGlyphs];
-
-    SkPaint paintCopy(paint);
-    x += MinikinUtils::xOffsetForTextAlign(&paintCopy, layout);
-    paintCopy.setTextAlign(SkPaint::kLeft_Align);
-    paintCopy.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
-    DrawTextFunctor f(layout, mCanvas, x, y, &paintCopy, glyphs, pos);
-    MinikinUtils::forFontRun(layout, &paintCopy, f);
-    drawTextDecorations(x, y, layout.getAdvance(), paintCopy);
-
-    delete[] glyphs;
-    delete[] pos;
-}
-
-// Same values used by Skia
-#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
-#define kStdUnderline_Offset    (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-void SkiaCanvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
-    uint32_t flags;
-    SkDrawFilter* drawFilter = mCanvas->getDrawFilter();
-    if (drawFilter) {
-        SkPaint paintCopy(paint);
-        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
-        flags = paintCopy.getFlags();
-    } else {
-        flags = paint.getFlags();
-    }
-    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
-        SkScalar left = x;
-        SkScalar right = x + length;
-        float textSize = paint.getTextSize();
-        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
-        if (flags & SkPaint::kUnderlineText_Flag) {
-            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
-            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
-            mCanvas->drawRectCoords(left, top, right, bottom, paint);
-        }
-        if (flags & SkPaint::kStrikeThruText_Flag) {
-            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
-            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
-            mCanvas->drawRectCoords(left, top, right, bottom, paint);
-        }
-    }
-}
-
-void SkiaCanvas::drawPosText(const char* text, const float* positions, int count, int posCount,
-        const SkPaint& paint) {
-    SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-    int indx;
-    for (indx = 0; indx < posCount; indx++) {
-        posPtr[indx].fX = positions[indx << 1];
-        posPtr[indx].fY = positions[(indx << 1) + 1];
-    }
-
-    SkPaint paintCopy(paint);
-    paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-    mCanvas->drawPosText(text, count, posPtr, paintCopy);
-
-    delete[] posPtr;
-}
-
-void SkiaCanvas::drawTextOnPath(const char* text, int count, const SkPath& path,
-        float hOffset, float vOffset, const SkPaint& paint) {
-    mCanvas->drawTextOnPathHV(text, count, path, hOffset, vOffset, paint);
-}
-
-} // namespace android
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 9436a47..3812c27 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -19,9 +19,9 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <vector>
 
-#include "Canvas.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 
+#include "SkCanvas.h"
 #include "SkDocument.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -132,9 +132,8 @@
         jint pageWidth, jint pageHeight,
         jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
-    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
-            contentLeft, contentTop, contentRight, contentBottom);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
+    return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
+            contentLeft, contentTop, contentRight, contentBottom));
 }
 
 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
deleted file mode 100644
index fd96a90..0000000
--- a/core/jni/android_graphics_Canvas.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "Canvas.h"
-#include "SkGraphics.h"
-#include "SkPorterDuff.h"
-#include "TypefaceImpl.h"
-
-#include <minikin/Layout.h>
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-
-namespace android {
-
-namespace CanvasJNI {
-
-static Canvas* get_canvas(jlong canvasHandle) {
-    return reinterpret_cast<Canvas*>(canvasHandle);
-}
-
-static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
-    delete get_canvas(canvasHandle);
-}
-
-// Native wrapper constructor used by Canvas(Bitmap)
-static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
-}
-
-// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
-// optionally copying canvas matrix & clip state.
-static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                      jboolean copyState) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
-}
-
-static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
-    return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
-}
-
-static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
-    return static_cast<jint>(get_canvas(canvasHandle)->width());
-}
-
-static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
-    return static_cast<jint>(get_canvas(canvasHandle)->height());
-}
-
-static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
-    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
-}
-
-static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
-    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-    return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
-}
-
-static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
-                      jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
-    SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
-    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-    return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
-}
-
-static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
-                           jfloat r, jfloat b, jint alpha, jint flagsHandle) {
-    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-    return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
-}
-
-static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
-    Canvas* canvas = get_canvas(canvasHandle);
-    if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
-        doThrowISE(env, "Underflow in restore");
-        return;
-    }
-    canvas->restore();
-}
-
-static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
-    Canvas* canvas = get_canvas(canvasHandle);
-    if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
-        doThrowIAE(env, "Underflow in restoreToCount");
-        return;
-    }
-    canvas->restoreToCount(restoreCount);
-}
-
-static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
-    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-    get_canvas(canvasHandle)->getMatrix(matrix);
-}
-
-static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
-    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-    get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
-}
-
-static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
-    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-    get_canvas(canvasHandle)->concat(*matrix);
-}
-
-static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
-    get_canvas(canvasHandle)->rotate(degrees);
-}
-
-static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
-    get_canvas(canvasHandle)->scale(sx, sy);
-}
-
-static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
-    get_canvas(canvasHandle)->skew(sx, sy);
-}
-
-static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
-    get_canvas(canvasHandle)->translate(dx, dy);
-}
-
-static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
-    SkRect   r;
-    SkIRect ir;
-    bool result = get_canvas(canvasHandle)->getClipBounds(&r);
-
-    if (!result) {
-        r.setEmpty();
-    }
-    r.round(&ir);
-
-    (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
-    return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
-                                jfloat left, jfloat top, jfloat right, jfloat bottom) {
-    bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
-    return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
-    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
-    return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
-                         jfloat r, jfloat b, jint opHandle) {
-    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
-    bool emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
-    return emptyClip ? JNI_FALSE : JNI_TRUE;
-}
-
-static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
-                         jint opHandle) {
-    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
-    bool emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
-    return emptyClip ? JNI_FALSE : JNI_TRUE;
-}
-
-static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
-                           jint opHandle) {
-    SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
-    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
-    bool emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
-    return emptyClip ? JNI_FALSE : JNI_TRUE;
-}
-
-static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
-     SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
-     get_canvas(canvasHandle)->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
-}
-
-static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawPaint(*paint);
-}
-
-static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
-                      jlong paintHandle) {
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawPoint(x, y, *paint);
-}
-
-static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
-                       jint offset, jint count, jlong paintHandle) {
-    NPE_CHECK_RETURN_VOID(env, jptsArray);
-    AutoJavaFloatArray autoPts(env, jptsArray);
-    float* floats = autoPts.ptr();
-    const int length = autoPts.length();
-
-    if ((offset | count) < 0 || offset + count > length) {
-        doThrowAIOOBE(env);
-        return;
-    }
-
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
-}
-
-static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
-                     jfloat stopX, jfloat stopY, jlong paintHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
-}
-
-static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
-                      jint offset, jint count, jlong paintHandle) {
-    NPE_CHECK_RETURN_VOID(env, jptsArray);
-    AutoJavaFloatArray autoPts(env, jptsArray);
-    float* floats = autoPts.ptr();
-    const int length = autoPts.length();
-
-    if ((offset | count) < 0 || offset + count > length) {
-        doThrowAIOOBE(env);
-        return;
-    }
-
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
-}
-
-static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-                     jfloat right, jfloat bottom, jlong paintHandle) {
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
-}
-
-static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
-}
-
-static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
-                       jfloat radius, jlong paintHandle) {
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
-}
-
-static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-                     jfloat right, jfloat bottom, jlong paintHandle) {
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
-}
-
-static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-                    jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
-                    jboolean useCenter, jlong paintHandle) {
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
-                                       useCenter, *paint);
-}
-
-static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
-                     jlong paintHandle) {
-    const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawPath(*path, *paint);
-}
-
-static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
-                         jint modeHandle, jint vertexCount,
-                         jfloatArray jverts, jint vertIndex,
-                         jfloatArray jtexs, jint texIndex,
-                         jintArray jcolors, jint colorIndex,
-                         jshortArray jindices, jint indexIndex,
-                         jint indexCount, jlong paintHandle) {
-    AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
-    AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
-    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
-    AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
-
-    const float* verts = vertA.ptr() + vertIndex;
-    const float* texs = texA.ptr() + vertIndex;
-    const int* colors = NULL;
-    const uint16_t* indices = NULL;
-
-    if (jcolors != NULL) {
-        colors = colorA.ptr() + colorIndex;
-    }
-    if (jindices != NULL) {
-        indices = (const uint16_t*)(indexA.ptr() + indexIndex);
-    }
-
-    SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
-                                           indices, indexCount, *paint);
-}
-
-static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jlong bitmapHandle,
-                       jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
-                       jint screenDensity, jint bitmapDensity) {
-    Canvas* canvas = get_canvas(canvasHandle);
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
-        if (screenDensity != 0 && screenDensity != bitmapDensity) {
-            SkPaint filteredPaint;
-            if (paint) {
-                filteredPaint = *paint;
-            }
-            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-            canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
-        } else {
-            canvas->drawBitmap(*bitmap, left, top, paint);
-        }
-    } else {
-        canvas->save(SkCanvas::kMatrixClip_SaveFlag);
-        SkScalar scale = canvasDensity / (float)bitmapDensity;
-        canvas->translate(left, top);
-        canvas->scale(scale, scale);
-
-        SkPaint filteredPaint;
-        if (paint) {
-            filteredPaint = *paint;
-        }
-        filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-
-        canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
-        canvas->restore();
-    }
-}
-
-static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                             jlong matrixHandle, jlong paintHandle) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawBitmap(*bitmap, *matrix, paint);
-}
-
-static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                           float srcLeft, float srcTop, float srcRight, float srcBottom,
-                           float dstLeft, float dstTop, float dstRight, float dstBottom,
-                           jlong paintHandle, jint screenDensity, jint bitmapDensity) {
-    Canvas* canvas = get_canvas(canvasHandle);
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-    if (screenDensity != 0 && screenDensity != bitmapDensity) {
-        SkPaint filteredPaint;
-        if (paint) {
-            filteredPaint = *paint;
-        }
-        filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
-                           dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
-    } else {
-        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
-                           dstLeft, dstTop, dstRight, dstBottom, paint);
-    }
-}
-
-static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
-                            jintArray jcolors, jint offset, jint stride,
-                            jfloat x, jfloat y, jint width, jint height,
-                            jboolean hasAlpha, jlong paintHandle) {
-    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
-    // correct the alphaType to kOpaque_SkAlphaType.
-    SkImageInfo info = SkImageInfo::Make(width, height,
-                           hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
-                           kPremul_SkAlphaType);
-    SkBitmap bitmap;
-    if (!bitmap.allocPixels(info)) {
-        return;
-    }
-
-    if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
-        return;
-    }
-
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
-}
-
-static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                           jint meshWidth, jint meshHeight, jfloatArray jverts,
-                           jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
-    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
-    AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
-    AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
-
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    get_canvas(canvasHandle)->drawBitmapMesh(*bitmap, meshWidth, meshHeight,
-                                             vertA.ptr(), colorA.ptr(), paint);
-}
-
-static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
-                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
-                          jlong paintHandle, jlong typefaceHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-    jchar* jchars = env->GetCharArrayElements(text, NULL);
-    const char* textArray = reinterpret_cast<const char*>(jchars) + index;
-    get_canvas(canvasHandle)->drawText(textArray, 0, count, count, x, y, bidiFlags, *paint, typeface);
-    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
-}
-
-static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
-                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
-                           jlong paintHandle, jlong typefaceHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-    const int count = end - start;
-    const jchar* jchars = env->GetStringChars(text, NULL);
-    const char* textArray = reinterpret_cast<const char*>(jchars) + start;
-    get_canvas(canvasHandle)->drawText(textArray, 0, count, count, x, y, bidiFlags, *paint, typeface);
-    env->ReleaseStringChars(text, jchars);
-}
-
-static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
-                             jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
-                             jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-    const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-    jchar* jchars = env->GetCharArrayElements(text, NULL);
-    const char* textArray = reinterpret_cast<const char*>(jchars) + contextIndex;
-    get_canvas(canvasHandle)->drawText(textArray, index - contextIndex, count, contextCount,
-                                       x, y, bidiFlags, *paint, typeface);
-    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
-}
-
-static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
-                              jint start, jint end, jint contextStart, jint contextEnd,
-                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
-                              jlong typefaceHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-    jint count = end - start;
-    jint contextCount = contextEnd - contextStart;
-    const jchar* jchars = env->GetStringChars(text, NULL);
-    const char* textArray = reinterpret_cast<const char*>(jchars) + contextStart;
-    get_canvas(canvasHandle)->drawText(textArray, start - contextStart, count, contextCount,
-                                       x, y, bidiFlags, *paint, typeface);
-    env->ReleaseStringChars(text, jchars);
-}
-
-static void drawPosTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
-                             jint index, jint count, jfloatArray pos, jlong paintHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    jchar* jchars = text ? env->GetCharArrayElements(text, NULL) : NULL;
-    float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
-    int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
-
-    const char* textArray = reinterpret_cast<const char*>(jchars) + index;
-    get_canvas(canvasHandle)->drawPosText(textArray, posArray, count << 1, posCount, *paint);
-
-    if (text) {
-        env->ReleaseCharArrayElements(text, jchars, 0);
-    }
-    if (pos) {
-        env->ReleaseFloatArrayElements(pos, posArray, 0);
-    }
-}
-
-
-static void drawPosTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
-                              jfloatArray pos, jlong paintHandle) {
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    const jchar* jchars = text ? env->GetStringChars(text, NULL) : NULL;
-    int byteLength = text ? env->GetStringLength(text) : 0;
-    float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
-    int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
-
-    const char* textArray = reinterpret_cast<const char*>(jchars);
-    get_canvas(canvasHandle)->drawPosText(textArray , posArray, byteLength << 1, posCount, *paint);
-
-    if (text) {
-        env->ReleaseStringChars(text, jchars);
-    }
-    if (pos) {
-        env->ReleaseFloatArrayElements(pos, posArray, 0);
-    }
-}
-
-class DrawTextOnPathFunctor {
-public:
-    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
-                float vOffset, const SkPaint& paint, const SkPath& path)
-            : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
-                paint(paint), path(path) {
-    }
-    void operator()(size_t start, size_t end) {
-        uint16_t glyphs[1];
-        for (size_t i = start; i < end; i++) {
-            glyphs[0] = layout.getGlyphId(i);
-            float x = hOffset + layout.getX(i);
-            float y = vOffset + layout.getY(i);
-            canvas->drawTextOnPath((const char*) glyphs, 1, path, x, y, paint);
-        }
-    }
-private:
-    const Layout& layout;
-    Canvas* canvas;
-    float hOffset;
-    float vOffset;
-    const SkPaint& paint;
-    const SkPath& path;
-};
-
-static void drawTextOnPath(Canvas* canvas, const char* text, int count, int bidiFlags,
-                           const SkPath& path, float hOffset, float vOffset,
-                           const SkPaint& paint, TypefaceImpl* typeface) {
-    SkPaint paintCopy(paint);
-    Layout layout;
-    std::string css = MinikinUtils::setLayoutProperties(&layout, &paintCopy, bidiFlags, typeface);
-    layout.doLayout((uint16_t*)text, 0, count, count, css);
-    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
-
-    // Set align to left for drawing, as we don't want individual
-    // glyphs centered or right-aligned; the offset above takes
-    // care of all alignment.
-    paintCopy.setTextAlign(SkPaint::kLeft_Align);
-
-    DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
-    MinikinUtils::forFontRun(layout, &paintCopy, f);
-}
-
-static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
-                                jint index, jint count, jlong pathHandle, jfloat hOffset,
-                                jfloat vOffset, jint bidiFlags, jlong paintHandle,
-                                jlong typefaceHandle) {
-    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-    jchar* jchars = env->GetCharArrayElements(text, NULL);
-    const char* textArray = reinterpret_cast<const char*>(jchars);
-
-    drawTextOnPath(get_canvas(canvasHandle), textArray + index, count, bidiFlags, *path,
-                   hOffset, vOffset, *paint, typeface);
-
-    env->ReleaseCharArrayElements(text, jchars, 0);
-}
-
-static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
-                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
-                                 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
-    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-    const jchar* jchars = env->GetStringChars(text, NULL);
-    const char* textArray = reinterpret_cast<const char*>(jchars);
-    int count = env->GetStringLength(text);
-
-    drawTextOnPath(get_canvas(canvasHandle), textArray, count, bidiFlags, *path,
-                   hOffset, vOffset, *paint, typeface);
-
-    env->ReleaseStringChars(text, jchars);
-}
-
-static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
-    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
-}
-
-static void freeCaches(JNIEnv* env, jobject) {
-    SkGraphics::PurgeFontCache();
-}
-
-static void freeTextLayoutCaches(JNIEnv* env, jobject) {
-    Layout::purgeCaches();
-}
-
-}; // namespace CanvasJNI
-
-static JNINativeMethod gMethods[] = {
-    {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
-    {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
-    {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
-    {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
-    {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
-    {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
-    {"native_save","(JI)I", (void*) CanvasJNI::save},
-    {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
-    {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
-    {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
-    {"native_restore","(J)V", (void*) CanvasJNI::restore},
-    {"native_restoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
-    {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
-    {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
-    {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
-    {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
-    {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
-    {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
-    {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
-    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
-    {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
-    {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
-    {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
-    {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
-    {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
-    {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
-    {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
-    {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
-    {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
-    {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
-    {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
-    {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
-    {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
-    {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
-    {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
-    {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
-    {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
-    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
-    {"native_drawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
-    {"nativeDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
-    {"native_drawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
-    {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
-    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
-    {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
-    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
-    {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
-    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
-    {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
-    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
-    {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
-    {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
-    {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
-};
-
-int register_android_graphics_Canvas(JNIEnv* env) {
-    return AndroidRuntime::registerNativeMethods(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
index eb8f6dd..f827907 100644
--- a/core/jni/android_graphics_Picture.cpp
+++ b/core/jni/android_graphics_Picture.cpp
@@ -51,7 +51,7 @@
 
 static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
                                           jlong pictureHandle) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
+    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
     Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
     SkASSERT(canvas);
     SkASSERT(picture);
@@ -84,7 +84,12 @@
 static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
                                                      jint w, jint h) {
     Picture* pict = reinterpret_cast<Picture*>(pictHandle);
-    Canvas* canvas = pict->beginRecording(w, h);
+    // beginRecording does not ref its return value, it just returns it.
+    SkCanvas* canvas = pict->beginRecording(w, h);
+    // the java side will wrap this guy in a Canvas.java, which will call
+    // unref in its finalizer, so we have to ref it here, so that both that
+    // Canvas.java and our picture can both be owners
+    canvas->ref();
     return reinterpret_cast<jlong>(canvas);
 }
 
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 658ef96..2b313b3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -834,9 +834,10 @@
         android:label="@string/permlab_changeWimaxState" />
 
     <!-- Allows applications to act as network scorers. @hide @SystemApi-->
+    <!-- TODO: Change protection level to normal when unhiding this API. -->
     <permission android:name="android.permission.SCORE_NETWORKS"
         android:permissionGroup="android.permission-group.NETWORK"
-        android:protectionLevel="normal"
+        android:protectionLevel="signature|system"
         android:description="@string/permdesc_scoreNetworks"
         android:label="@string/permlab_scoreNetworks" />
 
@@ -2770,6 +2771,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/drawable-hdpi/stat_notify_disabled.png b/core/res/res/drawable-hdpi/stat_notify_disabled.png
deleted file mode 100644
index c5e5917..0000000
--- a/core/res/res/drawable-hdpi/stat_notify_disabled.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_disabled.png b/core/res/res/drawable-mdpi/stat_notify_disabled.png
deleted file mode 100644
index c03277c..0000000
--- a/core/res/res/drawable-mdpi/stat_notify_disabled.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_notify_disabled.png b/core/res/res/drawable-xhdpi/stat_notify_disabled.png
deleted file mode 100644
index a99f1f4..0000000
--- a/core/res/res/drawable-xhdpi/stat_notify_disabled.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_disabled.png b/core/res/res/drawable-xxhdpi/stat_notify_disabled.png
deleted file mode 100644
index 06d7677b..0000000
--- a/core/res/res/drawable-xxhdpi/stat_notify_disabled.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_disabled_data.xml b/core/res/res/drawable/stat_notify_disabled_data.xml
new file mode 100644
index 0000000..d287a75
--- /dev/null
+++ b/core/res/res/drawable/stat_notify_disabled_data.xml
@@ -0,0 +1,37 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="24dp"
+        android:height="24dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M40.709,4.5l-6.604,7.337 0.0,16.601 6.604,6.604z"/>
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M32.305,13.838l-6.0629997,6.7370005 6.0629997,6.0629997z"/>
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M15.498,40.5l0.0,-7.9869995 -7.205,7.9869995z"/>
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M10.265,9.72l-2.5460005,2.545 9.971001,9.971 3.7139988,3.7140007 -4.105999,4.5619984 0.0,9.988001 6.6019993,0.0 0.0,-12.054001 1.8029995,1.8030014 0.0,10.250999 6.602001,0.0 0.0,-3.6479988 1.7999992,1.7999992 1.8479996,1.8479996 4.670002,4.669998 2.5459976,-2.5459976z"/>
+</vector>
diff --git a/core/res/res/layout-land/time_picker_holo.xml b/core/res/res/layout-land/time_picker_holo.xml
index f5ce1ec..1725ddc 100644
--- a/core/res/res/layout-land/time_picker_holo.xml
+++ b/core/res/res/layout-land/time_picker_holo.xml
@@ -33,8 +33,7 @@
         <FrameLayout
                 android:layout_width="match_parent"
                 android:layout_height="0dip"
-                android:layout_weight="1"
-                android:background="?android:attr/timePickerHeaderBackgroundColor">
+                android:layout_weight="1">
             <include
                     layout="@layout/time_header_label"
                     android:layout_width="match_parent"
@@ -47,7 +46,6 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical"
-                android:background="?android:attr/timePickerHeaderBackgroundColor"
                 android:divider="?android:attr/dividerHorizontal"
                 android:showDividers="beginning">
             <Button
diff --git a/core/res/res/layout/time_header_label.xml b/core/res/res/layout/time_header_label.xml
index f5f4a04..45c6241 100644
--- a/core/res/res/layout/time_header_label.xml
+++ b/core/res/res/layout/time_header_label.xml
@@ -25,8 +25,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_toLeftOf="@+id/separator"
-            android:layout_alignBaseline="@+id/separator"
-            android:textAppearance="?android:attr/timePickerHeaderTimeLabelTextAppearance"/>
+            android:layout_alignBaseline="@+id/separator"/>
 
     <TextView
             android:id="@+id/separator"
@@ -35,7 +34,6 @@
             android:paddingLeft="@dimen/timepicker_separator_padding"
             android:paddingRight="@dimen/timepicker_separator_padding"
             android:layout_centerInParent="true"
-            android:textAppearance="?android:attr/timePickerHeaderTimeLabelTextAppearance"
             android:importantForAccessibility="no" />
 
     <TextView
@@ -43,8 +41,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_toRightOf="@+id/separator"
-            android:layout_alignBaseline="@+id/separator"
-            android:textAppearance="?android:attr/timePickerHeaderTimeLabelTextAppearance" />
+            android:layout_alignBaseline="@+id/separator" />
 
     <TextView
             android:id="@+id/ampm_label"
@@ -53,7 +50,6 @@
             android:paddingLeft="@dimen/timepicker_ampm_left_padding"
             android:paddingRight="@dimen/timepicker_ampm_left_padding"
             android:layout_toRightOf="@+id/separator"
-            android:layout_alignBaseline="@+id/separator"
-            android:textAppearance="?android:attr/timePickerHeaderAmPmLabelTextAppearance" />
+            android:layout_alignBaseline="@+id/separator" />
 
 </RelativeLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 93d59b4..f57d61d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -676,15 +676,6 @@
         <!-- The TimePicker style. -->
         <attr name="timePickerStyle" format="reference" />
 
-        <!-- The TimePicker Header background color . -->
-        <attr name="timePickerHeaderBackgroundColor" format="reference" />
-
-        <!-- The TimePicker Header time label text appearance -->
-        <attr name="timePickerHeaderTimeLabelTextAppearance" format="reference" />
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <attr name="timePickerHeaderAmPmLabelTextAppearance" format="reference" />
-
         <!-- The TimePicker dialog theme. -->
         <attr name="timePickerDialogTheme" format="reference" />
 
@@ -4501,32 +4492,33 @@
     </declare-styleable>
 
     <declare-styleable name="TimePicker">
-        <!-- @hide The layout of the time picker. -->
-        <attr name="internalLayout" />
-        <!-- @hide The layout of the legacy time picker. -->
-        <attr name="legacyLayout" format="reference" />
         <!-- @hide Enables or disable the use of the legacy layout for the TimePicker. -->
         <attr name="legacyMode" format="boolean" />
-        <!-- @hide The color when the non legacy TimePicker is disabled. -->
-        <attr name="disabledColor" format="color|reference" />
-        <!-- @hide The color for selected text of the non legacy TimePicker. -->
-        <attr name="headerSelectedTextColor" format="color|reference" />
-        <!-- @hide The color for unselected text of the non legacy TimePicker. -->
-        <attr name="headerUnselectedTextColor" format="color|reference" />
-        <!-- @hide The background color for the header of the non legacy TimePicker. -->
-        <attr name="headerBackgroundColor" format="color|reference" />
-        <!-- @hide The color for the hours/minutes numbers of the non legacy TimePicker. -->
-        <attr name="numbersTextColor" format="color|reference" />
-        <!-- @hide The background color for the hours/minutes numbers of the non legacy TimePicker. -->
-        <attr name="numbersBackgroundColor" format="color|reference" />
-        <!-- @hide The color for the AM/PM selectors of the non legacy TimePicker. -->
-        <attr name="amPmTextColor" format="color|reference" />
-        <!-- @hide The background color for the AM/PM selectors of the non legacy TimePicker when unselected. -->
-        <attr name="amPmUnselectedBackgroundColor" format="color|reference" />
-        <!-- @hide The background color for the AM/PM selectors of the non legacy TimePicker when selected. -->
-        <attr name="amPmSelectedBackgroundColor" format="color|reference" />
-        <!-- @hide The color for the hours/minutes selector of the non legacy TimePicker. -->
-        <attr name="numbersSelectorColor" format="color|reference" />
+        <!-- @hide The layout of the legacy time picker. -->
+        <attr name="legacyLayout" format="reference" />
+        <!-- @hide The layout of the time picker. -->
+        <attr name="internalLayout" />
+        <!-- The text appearance for the AM/PM header of the TimePicker. -->
+        <attr name="headerAmPmTextAppearance" format="reference" />
+        <!-- The text appearance for the time header of the TimePicker. -->
+        <attr name="headerTimeTextAppearance" format="reference" />
+        <!-- The text color for selected time header of the TimePicker. This
+             will override the value from the text appearance. -->
+        <attr name="headerSelectedTextColor" format="color" />
+        <!-- The background color for the header of the TimePicker. -->
+        <attr name="headerBackgroundColor" format="color" />
+        <!-- The color for the hours/minutes numbers of the TimePicker. -->
+        <attr name="numbersTextColor" format="color" />
+        <!-- The background color for the hours/minutes numbers of the TimePicker. -->
+        <attr name="numbersBackgroundColor" format="color" />
+        <!-- The color for the AM/PM selectors of the TimePicker. -->
+        <attr name="amPmTextColor" format="color" />
+        <!-- The background color for the AM/PM selectors of the TimePicker when unselected. -->
+        <attr name="amPmUnselectedBackgroundColor" format="color" />
+        <!-- The background color for the AM/PM selectors of the TimePicker when selected. -->
+        <attr name="amPmSelectedBackgroundColor" format="color" />
+        <!-- The color for the hours/minutes selector of the TimePicker. -->
+        <attr name="numbersSelectorColor" format="color" />
     </declare-styleable>
 
     <!-- ========================= -->
@@ -5093,6 +5085,9 @@
         <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
              default value is src_in, which treats the drawable as an alpha mask. -->
         <attr name="tintMode" />
+        <!-- Indicates if the drawable needs to be mirrored when its layout direction is
+             RTL (right-to-left). -->
+        <attr name="autoMirrored" />
     </declare-styleable>
 
     <!-- Define the virtual size of the drawing surface paths will draw to. -->
@@ -6780,7 +6775,10 @@
          its {@link android.service.voice.VoiceInteractionService#SERVICE_META_DATA} meta-data entry.
          Described here are the attributes that can be included in that tag. -->
     <declare-styleable name="VoiceInteractionService">
+        <!-- The service that hosts active voice interaction sessions. -->
         <attr name="sessionService" format="string" />
+        <!-- The service that provides voice recognition. -->
+        <attr name="recognitionService" format="string" />
         <attr name="settingsActivity" />
     </declare-styleable>
 
@@ -6789,9 +6787,19 @@
          by the enrollment application.
          Described here are the attributes that can be included in that tag. -->
     <declare-styleable name="VoiceEnrollmentApplication">
+        <!-- A globally unique ID for the keyphrase. -->
         <attr name="searchKeyphraseId" format="integer" />
+        <!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. -->
         <attr name="searchKeyphrase" format="string" />
+        <!-- A comma separated list of java locales that are supported for this keyphrase,
+             or empty if not locale dependent. -->
         <attr name="searchKeyphraseSupportedLocales" format="string" />
+        <!-- Flags for supported recognition modes. -->
+        <attr name="searchKeyphraseRecognitionFlags">
+            <flag name="none" value="0" />
+            <flag name="voiceTrigger" value="0x1" />
+            <flag name="userIdentification" value="0x2" />
+        </attr>
     </declare-styleable>
 
     <!-- Attributes used to style the Action Bar. -->
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index 8785a567..eab1e3f 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -112,9 +112,6 @@
     <color name="timepicker_default_text_color_holo_light">#8c8c8c</color>
     <color name="timepicker_default_text_color_holo_dark">@color/white</color>
 
-    <color name="timepicker_default_disabled_color_holo_light">#7f000000</color>
-    <color name="timepicker_default_disabled_color_holo_dark">#7f08c8c8</color>
-
     <color name="timepicker_default_ampm_selected_background_color_holo_light">@color/holo_blue_light</color>
     <color name="timepicker_default_ampm_selected_background_color_holo_dark">@color/holo_blue_light</color>
 
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index c4f4891a..6988881 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -161,7 +161,6 @@
 
     <color name="timepicker_default_background_material">@color/primary_text_default_material_light</color>
     <color name="timepicker_default_text_color_material">@color/black</color>
-    <color name="timepicker_default_disabled_color_material">@color/bright_foreground_disabled_material_dark</color>
     <color name="timepicker_default_ampm_selected_background_color_material">@color/material_light_blue_A200</color>
     <color name="timepicker_default_ampm_unselected_background_color_material">@color/transparent</color>
     <color name="timepicker_default_selector_color_material">@color/material_light_blue_A200</color>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7b9a38f..e4484ad 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2250,6 +2250,23 @@
   <public type="attr" name="dateSelectorYearListItemTextAppearance" />
   <public type="attr" name="dateSelectorYearListSelectedCircleColor" />
   <public type="attr" name="calendarTextColor" />
+  <public type="attr" name="recognitionService" />
+
+  <!-- For the TimePicker -->
+  <public type="attr" name="timePickerStyle" />
+  <public type="attr" name="timePickerDialogTheme" />
+  <public type="attr" name="headerTimeTextAppearance" />
+  <public type="attr" name="headerAmPmTextAppearance" />
+  <public type="attr" name="headerSelectedTextColor" />
+  <public type="attr" name="headerBackgroundColor" />
+  <public type="attr" name="numbersTextColor" />
+  <public type="attr" name="numbersBackgroundColor" />
+  <public type="attr" name="numbersSelectorColor" />
+  <public type="attr" name="amPmTextColor" />
+  <public type="attr" name="amPmUnselectedBackgroundColor" />
+  <public type="attr" name="amPmSelectedBackgroundColor" />
+
+  <public type="attr" name="searchKeyphraseRecognitionFlags" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0285f54..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>
 
@@ -3979,16 +3984,6 @@
     <!-- Optional button to Skip a PreferenceActivity [CHAR LIMIT=20] -->
     <string name="skip_button_label">Skip</string>
 
-    <!-- Strings for throttling notification -->
-    <!-- Shown when the user is in danger of being throttled -->
-    <string name="throttle_warning_notification_title">High mobile data use</string>
-    <string name="throttle_warning_notification_message">Touch to learn more about mobile data use.</string>
-
-    <!-- Strings for throttling notification -->
-    <!-- Shown when the users bandwidth is reduced because of excessive data use -->
-    <string name="throttled_notification_title">Mobile data limit exceeded</string>
-    <string name="throttled_notification_message">Touch to learn more about mobile data use.</string>
-
     <!-- Displayed on the Find dialog when there are no matches [CHAR LIMIT=NONE]-->
     <string name="no_matches">No matches</string>
 
@@ -4198,22 +4193,22 @@
     <string name="data_usage_warning_body">Touch to view usage and settings.</string>
 
     <!-- Notification title when 2G-3G data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
-    <string name="data_usage_3g_limit_title">2G-3G data disabled</string>
+    <string name="data_usage_3g_limit_title">2G-3G data is off</string>
     <!-- Notification title when 4G data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
-    <string name="data_usage_4g_limit_title">4G data disabled</string>
+    <string name="data_usage_4g_limit_title">4G data is off</string>
     <!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
-    <string name="data_usage_mobile_limit_title">Mobile data disabled</string>
+    <string name="data_usage_mobile_limit_title">Cellular data is off</string>
     <!-- Notification title when Wi-Fi data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
-    <string name="data_usage_wifi_limit_title">Wi-Fi data disabled</string>
+    <string name="data_usage_wifi_limit_title">Wi-Fi data is off</string>
     <!-- Notification body when data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
-    <string name="data_usage_limit_body">Touch to enable.</string>
+    <string name="data_usage_limit_body">Limit reached</string>
 
     <!-- Notification title when 2G-3G data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_3g_limit_snoozed_title">2G-3G data limit exceeded</string>
     <!-- Notification title when 4G data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_4g_limit_snoozed_title">4G data limit exceeded</string>
     <!-- Notification title when mobile data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
-    <string name="data_usage_mobile_limit_snoozed_title">Mobile data limit exceeded</string>
+    <string name="data_usage_mobile_limit_snoozed_title">Cellular data limit exceeded</string>
     <!-- Notification title when Wi-Fi data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_wifi_limit_snoozed_title">Wi-Fi data limit exceeded</string>
     <!-- Notification body when data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 94ac19c..b9cacd7 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -568,6 +568,7 @@
     </style>
 
     <style name="Widget.TimePicker">
+        <item name="legacyMode">true</item>
         <item name="legacyLayout">@layout/time_picker_legacy</item>
     </style>
 
@@ -1331,11 +1332,8 @@
         <item name="orientation">vertical</item>
     </style>
 
-    <style name="TextAppearance.TimePicker.TimeLabel" parent="TextAppearance">
-    </style>
-
-    <style name="TextAppearance.TimePicker.AmPmLabel" parent="TextAppearance">
-    </style>
+    <style name="TextAppearance.TimePicker.TimeLabel" parent="TextAppearance" />
+    <style name="TextAppearance.TimePicker.AmPmLabel" parent="TextAppearance" />
 
     <style name="Widget.FastScroll">
         <item name="thumbDrawable">?attr/fastScrollThumbDrawable</item>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 9b5629b..fb8d9f5 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -212,8 +212,6 @@
     <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Material.Small.Inverse"/>
     <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title"/>
     <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle"/>
-    <style name="TextAppearance.DeviceDefault.TimePicker.TimeLabel" parent="TextAppearance.Material.TimePicker.TimeLabel"/>
-    <style name="TextAppearance.DeviceDefault.TimePicker.AmPmLabel" parent="TextAppearance.Material.TimePicker.AmPmLabel"/>
     <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget"/>
     <style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Material.Widget.Button"/>
     <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item"/>
@@ -281,9 +279,6 @@
     <style name="DeviceDefault.Light.ButtonBar.AlertDialog" parent="Widget.Material.Light.ButtonBar.AlertDialog"/>
     <style name="DeviceDefault.Light.SegmentedButton" parent="Widget.Material.Light.SegmentedButton"/>
 
-    <style name="TextAppearance.DeviceDefault.Light.TimePicker.TimeLabel" parent="TextAppearance.Material.TimePicker.TimeLabel"/>
-    <style name="TextAppearance.DeviceDefault.Light.TimePicker.AmPmLabel" parent="TextAppearance.Material.TimePicker.AmPmLabel"/>
-
     <style name="Theme.DeviceDefault.Dialog.TimePicker" parent="Theme.Material.Dialog.TimePicker"/>
     <style name="Theme.DeviceDefault.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.TimePicker"/>
 
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index 5dfbaed..bf5fd29 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -462,12 +462,14 @@
     </style>
 
     <style name="Widget.Holo.TimePicker" parent="Widget.TimePicker">
+        <item name="legacyMode">true</item>
         <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
+        <!-- Attributes for new-style TimePicker. -->
         <item name="internalLayout">@layout/time_picker_holo</item>
-        <item name="disabledColor">@color/timepicker_default_disabled_color_holo_dark</item>
-        <item name="headerSelectedTextColor">@color/holo_blue_light</item>
-        <item name="headerUnselectedTextColor">@color/timepicker_default_text_color_holo_dark</item>
+        <item name="headerTimeTextAppearance">@style/TextAppearance.Holo.TimePicker.TimeLabel</item>
+        <item name="headerAmPmTextAppearance">@style/TextAppearance.Holo.TimePicker.AmPmLabel</item>
         <item name="headerBackgroundColor">@color/timepicker_default_background_holo_dark</item>
+        <item name="headerSelectedTextColor">@color/holo_blue_light</item>
         <item name="numbersTextColor">@color/timepicker_default_text_color_holo_dark</item>
         <item name="numbersBackgroundColor">@color/timepicker_default_background_holo_dark</item>
         <item name="amPmTextColor">@color/timepicker_default_text_color_holo_dark</item>
@@ -880,12 +882,14 @@
     <style name="Widget.Holo.Light.NumberPicker" parent="Widget.Holo.NumberPicker" />
 
     <style name="Widget.Holo.Light.TimePicker" parent="Widget.TimePicker">
+        <item name="legacyMode">true</item>
         <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
+        <!-- Non-legacy styling -->
         <item name="internalLayout">@layout/time_picker_holo</item>
-        <item name="disabledColor">@color/timepicker_default_disabled_color_holo_light</item>
-        <item name="headerSelectedTextColor">@color/holo_blue_light</item>
-        <item name="headerUnselectedTextColor">@color/timepicker_default_text_color_holo_light</item>
+        <item name="headerTimeTextAppearance">@style/TextAppearance.Holo.Light.TimePicker.TimeLabel</item>
+        <item name="headerAmPmTextAppearance">@style/TextAppearance.Holo.Light.TimePicker.AmPmLabel</item>
         <item name="headerBackgroundColor">@color/timepicker_default_background_holo_light</item>
+        <item name="headerSelectedTextColor">@color/holo_blue_light</item>
         <item name="numbersTextColor">@color/timepicker_default_text_color_holo_light</item>
         <item name="numbersBackgroundColor">@color/timepicker_default_background_holo_light</item>
         <item name="amPmTextColor">@color/timepicker_default_text_color_holo_light</item>
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/styles_material.xml b/core/res/res/values/styles_material.xml
index 1d07c8d02..6ae4f90 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -343,13 +343,13 @@
 
     <style name="TextAppearance.Material.TimePicker.TimeLabel" parent="TextAppearance.Material">
         <item name="textSize">@dimen/timepicker_time_label_size</item>
-        <item name="textColor">?attr/textColorSecondary</item>
+        <item name="textColor">?attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Material.TimePicker.AmPmLabel" parent="TextAppearance.Material">
         <item name="textSize">@dimen/timepicker_ampm_label_size</item>
         <item name="textAllCaps">true</item>
-        <item name="textColor">?attr/textColorSecondary</item>
+        <item name="textColor">?attr/textColorPrimary</item>
         <item name="textStyle">bold</item>
     </style>
 
@@ -616,11 +616,11 @@
     </style>
 
     <style name="Widget.Material.TimePicker" parent="Widget.TimePicker">
-        <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
+        <item name="legacyMode">false</item>
         <item name="internalLayout">@layout/time_picker_holo</item>
-        <item name="disabledColor">@color/bright_foreground_disabled_material_dark</item>
+        <item name="headerTimeTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
+        <item name="headerAmPmTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
         <item name="headerSelectedTextColor">?attr/colorControlActivated</item>
-        <item name="headerUnselectedTextColor">?attr/textColorPrimary</item>
         <item name="headerBackgroundColor">@color/transparent</item>
         <item name="numbersTextColor">?attr/textColorSecondary</item>
         <item name="numbersBackgroundColor">@color/transparent</item>
@@ -956,12 +956,7 @@
     </style>
 
     <style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
-
-    <style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker">
-        <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
-        <item name="internalLayout">@layout/time_picker_holo</item>
-        <item name="disabledColor">@color/bright_foreground_disabled_material_light</item>
-    </style>
+    <style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker" />
 
     <style name="Widget.Material.Light.DatePicker" parent="Widget.DatePicker">
         <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 46e0194..aaadc16 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -876,6 +876,7 @@
   <java-symbol type="string" name="config_customResolverActivity" />
   <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
   <java-symbol type="string" name="error_message_title" />
+  <java-symbol type="string" name="app_no_restricted_accounts" />
   <java-symbol type="string" name="action_bar_home_description_format" />
   <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
   <java-symbol type="string" name="wireless_display_route_description" />
@@ -1502,7 +1503,7 @@
   <java-symbol type="drawable" name="ic_menu_refresh" />
   <java-symbol type="drawable" name="ic_settings" />
   <java-symbol type="drawable" name="stat_notify_car_mode" />
-  <java-symbol type="drawable" name="stat_notify_disabled" />
+  <java-symbol type="drawable" name="stat_notify_disabled_data" />
   <java-symbol type="drawable" name="stat_notify_disk_full" />
   <java-symbol type="drawable" name="stat_sys_adb" />
   <java-symbol type="drawable" name="stat_sys_battery" />
@@ -1846,17 +1847,13 @@
 
   <!-- From the new TimePicker and DatePicker -->
   <java-symbol type="attr" name="timePickerDialogTheme" />
-  <java-symbol type="attr" name="timePickerHeaderTimeLabelTextAppearance" />
-  <java-symbol type="attr" name="timePickerHeaderBackgroundColor" />
   <java-symbol type="attr" name="headerSelectedTextColor" />
-  <java-symbol type="attr" name="headerUnselectedTextColor" />
   <java-symbol type="attr" name="numbersTextColor" />
   <java-symbol type="attr" name="numbersBackgroundColor" />
   <java-symbol type="attr" name="amPmTextColor" />
   <java-symbol type="attr" name="amPmUnselectedBackgroundColor" />
   <java-symbol type="attr" name="amPmSelectedBackgroundColor" />
   <java-symbol type="attr" name="numbersSelectorColor" />
-  <java-symbol type="attr" name="timePickerHeaderTimeLabelTextAppearance" />
   <java-symbol type="attr" name="nestedScrollingEnabled" />
   <java-symbol type="attr" name="datePickerDialogTheme" />
   <java-symbol type="attr" name="datePickerHeaderSelectorBackgroundColor" />
@@ -1926,7 +1923,6 @@
   <java-symbol type="dimen" name="datepicker_year_picker_padding_top" />
 
   <java-symbol type="color" name="timepicker_default_text_color_holo_light" />
-  <java-symbol type="color" name="timepicker_default_disabled_color_holo_light" />
   <java-symbol type="color" name="timepicker_default_ampm_unselected_background_color_holo_light" />
   <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_holo_light" />
 
@@ -1955,7 +1951,6 @@
   <java-symbol type="style" name="Animation.VolumePanel" />
   <java-symbol type="transition" name="no_transition" />
   <java-symbol type="color" name="timepicker_default_text_color_material" />
-  <java-symbol type="color" name="timepicker_default_disabled_color_material" />
   <java-symbol type="color" name="timepicker_default_ampm_unselected_background_color_material" />
   <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_material" />
   <java-symbol type="color" name="timepicker_default_selector_color_material" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 0438ed1..0d4a2bf 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -394,15 +394,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.TimePicker</item>
 
-        <!-- TimePicker background color -->
-        <item name="timePickerHeaderBackgroundColor">@color/darker_gray</item>
-
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.Dialog.TimePicker</item>
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 2febbef..43381d6 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -195,12 +195,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.DeviceDefault.TimePicker</item>
 
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.DeviceDefault.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.DeviceDefault.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.DeviceDefault.Dialog.TimePicker</item>
 
@@ -470,12 +464,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.DeviceDefault.Light.TimePicker</item>
 
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.DeviceDefault.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.DeviceDefault.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.TimePicker</item>
 
diff --git a/core/res/res/values/themes_holo.xml b/core/res/res/values/themes_holo.xml
index dda42c1..f6c6b4c 100644
--- a/core/res/res/values/themes_holo.xml
+++ b/core/res/res/values/themes_holo.xml
@@ -374,15 +374,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.Holo.TimePicker</item>
 
-        <!-- TimePicker background color -->
-        <item name="timePickerHeaderBackgroundColor">@color/timepicker_default_background_holo_dark</item>
-
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Holo.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Holo.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.Holo.Dialog.TimePicker</item>
 
@@ -740,15 +731,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.Holo.Light.TimePicker</item>
 
-        <!-- TimePicker Header background color -->
-        <item name="timePickerHeaderBackgroundColor">@color/timepicker_default_background_holo_light</item>
-
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Holo.Light.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Holo.Light.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.Holo.Light.Dialog.TimePicker</item>
 
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index bdaeb45..741ffe6 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -352,15 +352,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.Material.TimePicker</item>
 
-        <!-- TimePicker background color -->
-        <item name="timePickerHeaderBackgroundColor">?colorBackground</item>
-
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.Material.Dialog.TimePicker</item>
 
@@ -727,15 +718,6 @@
         <!-- TimePicker style -->
         <item name="timePickerStyle">@style/Widget.Material.Light.TimePicker</item>
 
-        <!-- TimePicker Header background color -->
-        <item name="timePickerHeaderBackgroundColor">?attr/colorBackground</item>
-
-        <!-- TimePicker Header time label text appearance -->
-        <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
-
-        <!-- TimePicker Header am pm label text appearance -->
-        <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
-
         <!-- TimePicker dialog theme -->
         <item name="timePickerDialogTheme">@style/Theme.Material.Light.Dialog.TimePicker</item>
 
@@ -1153,12 +1135,8 @@
          AlertDialog theme. -->
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert"/>
 
-    <style name="Theme.Material.Dialog.BaseTimePicker">
-        <item name="windowBackground">@color/transparent</item>
-        <item name="windowElevation">0dp</item>
-        <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
-        <item name="windowContentOverlay">@null</item>
-    </style>
+    <!-- Base theme used to prevent circular references in Leanback overrides. -->
+    <style name="Theme.Material.Dialog.BaseTimePicker" />
 
     <!-- Material theme for the TimePicker dialog windows, which is used by the
          {@link android.app.TimePickerDialog} class. -->
@@ -1282,16 +1260,12 @@
          AlertDialog theme. -->
     <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert"/>
 
-    <style name="Theme.Material.Light.Dialog.BaseTimePicker">
-        <item name="windowBackground">@color/transparent</item>
-        <item name="windowElevation">0dp</item>
-        <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
-        <item name="windowContentOverlay">@null</item>
-    </style>
+    <!-- Base theme used to prevent circular references in Leanback overrides. -->
+    <style name="Theme.Material.Light.Dialog.BaseTimePicker" />
 
     <!-- Material Light theme for the TimePicker dialog windows, which is used by the
          {@link android.app.TimePickerDialog} class. -->
-    <style name="Theme.Material.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker"/>
+    <style name="Theme.Material.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker" />
 
     <style name="Theme.Material.Light.Dialog.BaseDatePicker">
         <item name="windowBackground">@color/transparent</item>
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
index e09dcbf..882f421 100644
--- a/docs/html/preview/tv/adt-1/index.jd
+++ b/docs/html/preview/tv/adt-1/index.jd
@@ -139,15 +139,15 @@
   <li>YouTube</li>
   <li>Netflix</li>
   <li>Google+ Photos</li>
-  <li>Google Play Movies and TV (Android only)</li>
+  <li>Google Play Movies and TV (Android and iOS only)</li>
+  <li>Google Play Music</li>
+  <li>Mirror your Android device screen to ADT-1</li>
 </ul>
 
 <p>Coming soon:</p>
 
 <ul>
-  <li>Google Play Music</li>
-  <li>Google Play Movies and TV (iOS and Chrome)</li>
-  <li>Mirror you Android device screen to ADT-1</li>
+  <li>Google Play Movies and TV (Chrome)</li>
 </ul>
 
 <p class="note">
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index ef4b260..72e2056 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -158,7 +158,7 @@
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
         }
-        mNativeCanvasWrapper = nativeCanvas;
+        mNativeCanvasWrapper = initCanvas(nativeCanvas);
         mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mDensity = Bitmap.getDefaultDensity();
     }
@@ -924,7 +924,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawRGB(int r, int g, int b) {
-        drawColor(Color.rgb(r, g, b));
+        native_drawRGB(mNativeCanvasWrapper, r, g, b);
     }
 
     /**
@@ -937,7 +937,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawARGB(int a, int r, int g, int b) {
-        drawColor(Color.argb(a, r, g, b));
+        native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
     }
 
     /**
@@ -947,7 +947,7 @@
      * @param color the color to draw onto the canvas
      */
     public void drawColor(int color) {
-        native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
+        native_drawColor(mNativeCanvasWrapper, color);
     }
 
     /**
@@ -1301,28 +1301,13 @@
      */
     public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
             @Nullable Paint paint) {
-      if (dst == null) {
-          throw new NullPointerException();
-      }
-      throwIfCannotDraw(bitmap);
-      final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-
-      float left, top, right, bottom;
-      if (src == null) {
-          left = top = 0;
-          right = bitmap.getWidth();
-          bottom = bitmap.getHeight();
-      } else {
-          left = src.left;
-          right = src.right;
-          top = src.top;
-          bottom = src.bottom;
-      }
-
-      native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
-              dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
-              bitmap.mDensity);
-  }
+        if (dst == null) {
+            throw new NullPointerException();
+        }
+        throwIfCannotDraw(bitmap);
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
+                          paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+    }
 
     /**
      * Draw the specified bitmap, scaling/translating automatically to fill
@@ -1352,23 +1337,8 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-
-        int left, top, right, bottom;
-        if (src == null) {
-            left = top = 0;
-            right = bitmap.getWidth();
-            bottom = bitmap.getHeight();
-        } else {
-            left = src.left;
-            right = src.right;
-            top = src.top;
-            bottom = src.bottom;
-        }
-
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
-            dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
-            bitmap.mDensity);
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
+                paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
     }
 
     /**
@@ -1896,6 +1866,7 @@
     public static native void freeTextLayoutCaches();
 
     private static native long initRaster(long nativeBitmapOrZero);
+    private static native long initCanvas(long canvasHandle);
     private static native void native_setBitmap(long canvasHandle,
                                                 long bitmapHandle,
                                                 boolean copyState);
@@ -1948,6 +1919,11 @@
     private static native boolean native_quickReject(long nativeCanvas,
                                                      float left, float top,
                                                      float right, float bottom);
+    private static native void native_drawRGB(long nativeCanvas, int r, int g,
+                                              int b);
+    private static native void native_drawARGB(long nativeCanvas, int a, int r,
+                                               int g, int b);
+    private static native void native_drawColor(long nativeCanvas, int color);
     private static native void native_drawColor(long nativeCanvas, int color,
                                                 int mode);
     private static native void native_drawPaint(long nativeCanvas,
@@ -1989,9 +1965,16 @@
                                                  int screenDensity,
                                                  int bitmapDensity);
     private native void native_drawBitmap(long nativeCanvas, long nativeBitmap,
-            float srcLeft, float srcTop, float srcRight, float srcBottom,
-            float dstLeft, float dstTop, float dstRight, float dstBottom,
-            long nativePaintOrZero, int screenDensity, int bitmapDensity);
+                                                 Rect src, RectF dst,
+                                                 long nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity);
+    private static native void native_drawBitmap(long nativeCanvas,
+                                                 long nativeBitmap,
+                                                 Rect src, Rect dst,
+                                                 long nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity);
     private static native void native_drawBitmap(long nativeCanvas, int[] colors,
                                                 int offset, int stride, float x,
                                                  float y, int width, int height,
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 8c907b2..43e6509 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -33,6 +33,7 @@
 import android.graphics.PorterDuff.Mode;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.LayoutDirection;
 import android.util.Log;
 import android.util.PathParser;
 import android.util.Xml;
@@ -187,7 +188,13 @@
     public void draw(Canvas canvas) {
         final int saveCount = canvas.save();
         final Rect bounds = getBounds();
+        final boolean needMirroring = needMirroring();
+
         canvas.translate(bounds.left, bounds.top);
+        if (needMirroring) {
+            canvas.translate(bounds.width(), 0);
+            canvas.scale(-1.0f, 1.0f);
+        }
 
         if (!mAllowCaching) {
             mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
@@ -205,6 +212,7 @@
             }
             canvas.drawBitmap(bitmap, null, bounds, null);
         }
+
         canvas.restoreToCount(saveCount);
     }
 
@@ -352,6 +360,9 @@
         if (tint != null) {
             state.mTint = tint;
         }
+
+        state.mAutoMirrored = a.getBoolean(
+                R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored);
     }
 
     private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
@@ -469,12 +480,30 @@
         mAllowCaching = allowCaching;
     }
 
+    private boolean needMirroring() {
+        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        if (mVectorState.mAutoMirrored != mirrored) {
+            mVectorState.mAutoMirrored = mirrored;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mVectorState.mAutoMirrored;
+    }
+
     private static class VectorDrawableState extends ConstantState {
         int[] mThemeAttrs;
         int mChangingConfigurations;
         VPathRenderer mVPathRenderer;
         ColorStateList mTint;
         Mode mTintMode;
+        boolean mAutoMirrored;
 
         Bitmap mCachedBitmap;
         int[] mCachedThemeAttrs;
@@ -490,6 +519,7 @@
                 mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
                 mTint = copy.mTint;
                 mTintMode = copy.mTintMode;
+                mAutoMirrored = copy.mAutoMirrored;
             }
         }
 
@@ -497,6 +527,7 @@
             if (mCachedThemeAttrs == mThemeAttrs
                     && mCachedTint == mTint
                     && mCachedTintMode == mTintMode
+                    && mAutoMirrored == mAutoMirrored
                     && width == mCachedBitmap.getWidth()
                     && height == mCachedBitmap.getHeight()
                     && mCachedRootAlpha == mVPathRenderer.getRootAlpha())  {
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 610528c..99b3195 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -278,6 +278,7 @@
     const ResTable* getResTable(bool required = true) const;
     void setLocaleLocked(const char* locale);
     void updateResourceParamsLocked() const;
+    bool appendPathToResTable(const asset_path& ap) const;
 
     Asset* openIdmapLocked(const struct asset_path& ap) const;
 
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 4ba20d7..de6a33cf 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -231,6 +231,10 @@
     }
 #endif
 
+    if (mResources != NULL) {
+        appendPathToResTable(ap);
+    }
+
     return true;
 }
 
@@ -596,6 +600,96 @@
         return kFileTypeRegular;
 }
 
+bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+    Asset* ass = NULL;
+    ResTable* sharedRes = NULL;
+    bool shared = true;
+    bool onlyEmptyResources = true;
+    MY_TRACE_BEGIN(ap.path.string());
+    Asset* idmap = openIdmapLocked(ap);
+    size_t nextEntryIdx = mResources->getTableCount();
+    ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
+    if (ap.type != kFileTypeDirectory) {
+        if (nextEntryIdx == 0) {
+            // The first item is typically the framework resources,
+            // which we want to avoid parsing every time.
+            sharedRes = const_cast<AssetManager*>(this)->
+                mZipSet.getZipResourceTable(ap.path);
+            if (sharedRes != NULL) {
+                // skip ahead the number of system overlay packages preloaded
+                nextEntryIdx = sharedRes->getTableCount();
+            }
+        }
+        if (sharedRes == NULL) {
+            ass = const_cast<AssetManager*>(this)->
+                mZipSet.getZipResourceTableAsset(ap.path);
+            if (ass == NULL) {
+                ALOGV("loading resource table %s\n", ap.path.string());
+                ass = const_cast<AssetManager*>(this)->
+                    openNonAssetInPathLocked("resources.arsc",
+                                             Asset::ACCESS_BUFFER,
+                                             ap);
+                if (ass != NULL && ass != kExcludedAsset) {
+                    ass = const_cast<AssetManager*>(this)->
+                        mZipSet.setZipResourceTableAsset(ap.path, ass);
+                }
+            }
+            
+            if (nextEntryIdx == 0 && ass != NULL) {
+                // If this is the first resource table in the asset
+                // manager, then we are going to cache it so that we
+                // can quickly copy it out for others.
+                ALOGV("Creating shared resources for %s", ap.path.string());
+                sharedRes = new ResTable();
+                sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
+#ifdef HAVE_ANDROID_OS
+                const char* data = getenv("ANDROID_DATA");
+                LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+                String8 overlaysListPath(data);
+                overlaysListPath.appendPath(kResourceCache);
+                overlaysListPath.appendPath("overlays.list");
+                addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
+#endif
+                sharedRes = const_cast<AssetManager*>(this)->
+                    mZipSet.setZipResourceTable(ap.path, sharedRes);
+            }
+        }
+    } else {
+        ALOGV("loading resource table %s\n", ap.path.string());
+        ass = const_cast<AssetManager*>(this)->
+            openNonAssetInPathLocked("resources.arsc",
+                                     Asset::ACCESS_BUFFER,
+                                     ap);
+        shared = false;
+    }
+
+    if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
+        ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
+        if (sharedRes != NULL) {
+            ALOGV("Copying existing resources for %s", ap.path.string());
+            mResources->add(sharedRes);
+        } else {
+            ALOGV("Parsing resources for %s", ap.path.string());
+            mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
+        }
+        onlyEmptyResources = false;
+
+        if (!shared) {
+            delete ass;
+        }
+    } else {
+        ALOGV("Installing empty resources in to table %p\n", mResources);
+        mResources->addEmpty(nextEntryIdx + 1);
+    }
+
+    if (idmap != NULL) {
+        delete idmap;
+    }
+    MY_TRACE_END();
+
+    return onlyEmptyResources;
+}
+
 const ResTable* AssetManager::getResTable(bool required) const
 {
     ResTable* rt = mResources;
@@ -625,90 +719,8 @@
     bool onlyEmptyResources = true;
     const size_t N = mAssetPaths.size();
     for (size_t i=0; i<N; i++) {
-        Asset* ass = NULL;
-        ResTable* sharedRes = NULL;
-        bool shared = true;
-        const asset_path& ap = mAssetPaths.itemAt(i);
-        MY_TRACE_BEGIN(ap.path.string());
-        Asset* idmap = openIdmapLocked(ap);
-        ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
-        if (ap.type != kFileTypeDirectory) {
-            if (i == 0) {
-                // The first item is typically the framework resources,
-                // which we want to avoid parsing every time.
-                sharedRes = const_cast<AssetManager*>(this)->
-                    mZipSet.getZipResourceTable(ap.path);
-                if (sharedRes != NULL) {
-                    // skip ahead the number of system overlay packages preloaded
-                    i += sharedRes->getTableCount() - 1;
-                }
-            }
-            if (sharedRes == NULL) {
-                ass = const_cast<AssetManager*>(this)->
-                    mZipSet.getZipResourceTableAsset(ap.path);
-                if (ass == NULL) {
-                    ALOGV("loading resource table %s\n", ap.path.string());
-                    ass = const_cast<AssetManager*>(this)->
-                        openNonAssetInPathLocked("resources.arsc",
-                                                 Asset::ACCESS_BUFFER,
-                                                 ap);
-                    if (ass != NULL && ass != kExcludedAsset) {
-                        ass = const_cast<AssetManager*>(this)->
-                            mZipSet.setZipResourceTableAsset(ap.path, ass);
-                    }
-                }
-                
-                if (i == 0 && ass != NULL) {
-                    // If this is the first resource table in the asset
-                    // manager, then we are going to cache it so that we
-                    // can quickly copy it out for others.
-                    ALOGV("Creating shared resources for %s", ap.path.string());
-                    sharedRes = new ResTable();
-                    sharedRes->add(ass, idmap, i + 1, false);
-#ifdef HAVE_ANDROID_OS
-                    const char* data = getenv("ANDROID_DATA");
-                    LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
-                    String8 overlaysListPath(data);
-                    overlaysListPath.appendPath(kResourceCache);
-                    overlaysListPath.appendPath("overlays.list");
-                    addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, i);
-#endif
-                    sharedRes = const_cast<AssetManager*>(this)->
-                        mZipSet.setZipResourceTable(ap.path, sharedRes);
-                }
-            }
-        } else {
-            ALOGV("loading resource table %s\n", ap.path.string());
-            ass = const_cast<AssetManager*>(this)->
-                openNonAssetInPathLocked("resources.arsc",
-                                         Asset::ACCESS_BUFFER,
-                                         ap);
-            shared = false;
-        }
-
-        if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
-            ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
-            if (sharedRes != NULL) {
-                ALOGV("Copying existing resources for %s", ap.path.string());
-                mResources->add(sharedRes);
-            } else {
-                ALOGV("Parsing resources for %s", ap.path.string());
-                mResources->add(ass, idmap, i + 1, !shared);
-            }
-            onlyEmptyResources = false;
-
-            if (!shared) {
-                delete ass;
-            }
-        } else {
-            ALOGV("Installing empty resources in to table %p\n", mResources);
-            mResources->addEmpty(i + 1);
-        }
-
-        if (idmap != NULL) {
-            delete idmap;
-        }
-        MY_TRACE_END();
+        bool empty = appendPathToResTable(mAssetPaths.itemAt(i));
+        onlyEmptyResources = onlyEmptyResources && empty;
     }
 
     if (required && onlyEmptyResources) {
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 8e3c444..548ec91 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -198,6 +198,7 @@
         drawDeferDisabled = !strcasecmp(property, "true");
         INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
     } else {
+        drawDeferDisabled = false;
         INIT_LOGD("  Draw defer enabled");
     }
 
@@ -205,6 +206,7 @@
         drawReorderDisabled = !strcasecmp(property, "true");
         INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
     } else {
+        drawReorderDisabled = false;
         INIT_LOGD("  Draw reorder enabled");
     }
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 75d52b4..9f66904 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -21,6 +21,7 @@
     #define LOG_TAG "OpenGLRenderer"
 #endif
 
+#include <SkColor.h>
 #include <SkPath.h>
 #include <SkPathOps.h>
 #include <SkXfermode.h>
@@ -1315,7 +1316,7 @@
         FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
         fontRenderer.precache(paint, mText, mCount, SkMatrix::I());
 
-        deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+        deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
                 DeferredDisplayList::kOpBatch_Text :
                 DeferredDisplayList::kOpBatch_ColorText;
     }
@@ -1385,17 +1386,19 @@
             fontRenderer.precache(paint, mText, mCount, transform);
             mPrecacheTransform = transform;
         }
-        deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+        deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
                 DeferredDisplayList::kOpBatch_Text :
                 DeferredDisplayList::kOpBatch_ColorText;
 
         deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor());
 
         // don't merge decorated text - the decorations won't draw in order
-        bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
-                        SkPaint::kStrikeThruText_Flag));
-        deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations &&
-                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+        bool hasDecorations = mPaint->getFlags()
+                & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag);
+
+        deferInfo.mergeable = state.mMatrix.isPureTranslate()
+                && !hasDecorations
+                && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
     }
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 278dd75..5a96132 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <SkCanvas.h>
+#include <SkColor.h>
 #include <SkShader.h>
 #include <SkTypeface.h>
 
@@ -1476,7 +1477,7 @@
             mSkipOutlineClip = true;
 
             SkPaint paint;
-            paint.setColor(0xff000000);
+            paint.setColor(SK_ColorBLACK);
             paint.setXfermodeMode(SkXfermode::kSrc_Mode);
 
             // NOTE: We could use the region contour path to generate a smaller mesh
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index ae48608..9ba8854 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -19,6 +19,7 @@
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
+#include <SkColor.h>
 #include <SkPaint.h>
 #include <SkPath.h>
 #include <SkRect.h>
@@ -111,7 +112,7 @@
 static void initPaint(SkPaint& paint) {
     // Make sure the paint is opaque, color, alpha, filter, etc.
     // will be applied later when compositing the alpha8 texture
-    paint.setColor(0xff000000);
+    paint.setColor(SK_ColorBLACK);
     paint.setAlpha(255);
     paint.setColorFilter(NULL);
     paint.setMaskFilter(NULL);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a4ac262..2147810 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -185,7 +185,7 @@
     } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
         dirty.setEmpty();
     } else {
-        if (!dirty.intersect(0, 0, width, height)) {
+        if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
             ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
                     SK_RECT_ARGS(dirty), width, height);
             dirty.setEmpty();
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/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/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index ffb6850..8ecf808 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,7 +16,6 @@
 
 package android.media.tv;
 
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.media.tv.TvContract.Programs;
@@ -121,26 +120,23 @@
     /**
      * Builds a URI that points to all browsable channels from a given TV input.
      *
-     * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
-     *            implements the given TV input.
+     * @param inputId The ID of the TV input to build a channels URI for.
      */
-    public static final Uri buildChannelsUriForInput(ComponentName name) {
-        return buildChannelsUriForInput(name, true);
+    public static final Uri buildChannelsUriForInput(String inputId) {
+        return buildChannelsUriForInput(inputId, true);
     }
 
     /**
      * Builds a URI that points to all or browsable-only channels from a given TV input.
      *
-     * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
-     *            implements the given TV input.
+     * @param inputId The ID of the TV input to build a channels URI for.
      * @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.
      */
-    public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
+    public static final Uri buildChannelsUriForInput(String inputId, boolean browsableOnly) {
         return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
-                .appendPath(PATH_INPUT).appendPath(name.getPackageName())
-                .appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
+                .appendPath(PATH_INPUT).appendPath(inputId).appendPath(PATH_CHANNEL)
                 .appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
     }
 
@@ -148,26 +144,26 @@
      * Builds a URI that points to all or browsable-only channels which have programs with the given
      * genre from the given TV input.
      *
-     * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
-     *            implements the given TV input. If null, builds a URI for all the TV inputs.
+     * @param inputId The ID of the TV input to build a channels URI for. If null, builds a URI for
+     *            all the TV inputs.
      * @param genre {@link Programs.Genres} to search.
      * @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 buildChannelsUriForCanonicalGenre(ComponentName name, String genre,
+    public static final Uri buildChannelsUriForCanonicalGenre(String inputId, String genre,
             boolean browsableOnly) {
         if (!Programs.Genres.isCanonical(genre)) {
             throw new IllegalArgumentException("Not a canonical genre: '" + genre + "'");
         }
 
         Uri uri;
-        if (name == null) {
+        if (inputId == null) {
             uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
                     .appendPath(PATH_CHANNEL).build();
         } else {
-            uri = buildChannelsUriForInput(name, browsableOnly);
+            uri = buildChannelsUriForInput(inputId, browsableOnly);
         }
         return uri.buildUpon().appendQueryParameter(PARAM_CANONICAL_GENRE, genre).build();
     }
@@ -250,42 +246,25 @@
     }
 
     /**
-     * Extracts the {@link Channels#COLUMN_PACKAGE_NAME} from a given URI.
+     * Extracts the {@link Channels#COLUMN_INPUT_ID} from a given URI.
      *
-     * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
-     *            {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+     * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(String)},
+     *            {@link #buildChannelsUriForInput(String, boolean)}, or
+     *            {@link #buildChannelsUriForCanonicalGenre(String, String, boolean)}.
      * @hide
      */
-    public static final String getPackageName(Uri channelsUri) {
+    public static final String getInputId(Uri channelsUri) {
         final List<String> paths = channelsUri.getPathSegments();
-        if (paths.size() < 4) {
+        if (paths.size() < 3) {
             throw new IllegalArgumentException("Not channels: " + channelsUri);
         }
-        if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+        if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(2))) {
             throw new IllegalArgumentException("Not channels: " + channelsUri);
         }
         return paths.get(1);
     }
 
     /**
-     * Extracts the {@link Channels#COLUMN_SERVICE_NAME} from a given URI.
-     *
-     * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
-     *            {@link #buildChannelsUriForInput(ComponentName, boolean)}.
-     * @hide
-     */
-    public static final String getServiceName(Uri channelsUri) {
-        final List<String> paths = channelsUri.getPathSegments();
-        if (paths.size() < 4) {
-            throw new IllegalArgumentException("Not channels: " + channelsUri);
-        }
-        if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
-            throw new IllegalArgumentException("Not channels: " + channelsUri);
-        }
-        return paths.get(2);
-    }
-
-    /**
      * Extracts the {@link Channels#_ID} from a given URI.
      *
      * @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
@@ -496,15 +475,14 @@
         }
 
         /**
-         * The name of the {@link TvInputService} subclass that provides this TV channel. This
-         * should be a fully qualified class name (such as, "com.example.project.TvInputService").
+         * The ID of the TV input that provides this TV channel.
          * <p>
          * This is a required field.
          * </p><p>
          * Type: TEXT
          * </p>
          */
-        public static final String COLUMN_SERVICE_NAME = "service_name";
+        public static final String COLUMN_INPUT_ID = "input_id";
 
         /**
          * The predefined type of this TV channel.
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 5624f3e..37f166b 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -68,14 +68,15 @@
     public static final int TYPE_PASSTHROUGH = 3;
 
     /**
-     * The name of the TV input service to provide to the setup activity and settings activity.
+     * The ID of the TV input to provide to the setup activity and settings activity.
      */
-    public static final String EXTRA_SERVICE_NAME = "serviceName";
+    public static final String EXTRA_INPUT_ID = "inputId";
 
     private static final String XML_START_TAG_NAME = "tv-input";
 
     private final ResolveInfo mService;
     private final String mId;
+    private final String mParentId;
 
     // Attributes from XML meta data.
     private String mSetupActivity;
@@ -114,7 +115,7 @@
                         "Meta-data does not start with tv-input-service tag in " + si.name);
             }
 
-            TvInputInfo input = new TvInputInfo(context, service);
+            TvInputInfo input = new TvInputInfo(context, service, null);
             TypedArray sa = res.obtainAttributes(attrs,
                     com.android.internal.R.styleable.TvInputService);
             input.mSetupActivity = sa.getString(
@@ -154,10 +155,11 @@
      * @param service The ResolveInfo returned from the package manager about this TV input service.
      * @hide
      */
-    private TvInputInfo(Context context, ResolveInfo service) {
+    private TvInputInfo(Context context, ResolveInfo service, String parentId) {
         mService = service;
         ServiceInfo si = service.serviceInfo;
         mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
+        mParentId = parentId;
     }
 
     /**
@@ -169,6 +171,24 @@
     }
 
     /**
+     * Returns the parent input ID.
+     * <p>
+     * When a part of the functionalities of a TV input is actually provided by another TV input,
+     * we can describe this relationship as the depending input having a "parent". It is primarily
+     * used for controlling underlying hardware when the current input itself does not have direct
+     * access to it. Examples include a TV input for a specific HDMI CEC logical device having a
+     * generic HDMI input as its parent and a HDMI-paired virtual input whose video stream comes
+     * from an external settop box. Applications may group inputs by parent ID to provide an easier
+     * access to similar inputs.
+     *
+     * @return the ID of the parent input, if exists. Returns {@code null} if the parent input is
+     *         not specified.
+     */
+    public String getParentId() {
+        return mParentId;
+    }
+
+    /**
      * Returns the information of the service that implements this TV input.
      */
     public ServiceInfo getServiceInfo() {
@@ -190,7 +210,7 @@
         if (!TextUtils.isEmpty(mSetupActivity)) {
             Intent intent = new Intent(Intent.ACTION_MAIN);
             intent.setClassName(mService.serviceInfo.packageName, mSetupActivity);
-            intent.putExtra(EXTRA_SERVICE_NAME, mService.serviceInfo.name);
+            intent.putExtra(EXTRA_INPUT_ID, getId());
             return intent;
         }
         return null;
@@ -203,7 +223,7 @@
         if (!TextUtils.isEmpty(mSettingsActivity)) {
             Intent intent = new Intent(Intent.ACTION_MAIN);
             intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity);
-            intent.putExtra(EXTRA_SERVICE_NAME, mService.serviceInfo.name);
+            intent.putExtra(EXTRA_INPUT_ID, getId());
             return intent;
         }
         return null;
@@ -260,9 +280,7 @@
         }
 
         TvInputInfo obj = (TvInputInfo) o;
-        return mId.equals(obj.mId)
-                && mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
-                && mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
+        return mId.equals(obj.mId);
     }
 
     @Override
@@ -281,9 +299,11 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mId);
+        dest.writeString(mParentId);
         mService.writeToParcel(dest, flags);
         dest.writeString(mSetupActivity);
         dest.writeString(mSettingsActivity);
+        dest.writeInt(mType);
     }
 
     /**
@@ -317,8 +337,10 @@
 
     private TvInputInfo(Parcel in) {
         mId = in.readString();
+        mParentId = in.readString();
         mService = ResolveInfo.CREATOR.createFromParcel(in);
         mSetupActivity = in.readString();
         mSettingsActivity = in.readString();
+        mType = in.readInt();
     }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b7210e1..a038899 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -70,6 +70,7 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
     <uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
@@ -108,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"
@@ -244,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/color/qs_user_detail_name.xml b/packages/SystemUI/res/color/qs_user_detail_name.xml
new file mode 100644
index 0000000..8ddb9be
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_name.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:color="#ffffffff" />
+    <item android:color="#66ffffff" /> <!-- 40% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_account_circle_qs.xml b/packages/SystemUI/res/drawable/ic_account_circle_qs.xml
new file mode 100644
index 0000000..0d5cd2e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_account_circle_qs.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:state_activated="true" android:drawable="@drawable/ic_account_circle" />
+    <item android:drawable="@drawable/ic_account_circle_qs_muted" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_account_circle_qs_muted.xml b/packages/SystemUI/res/drawable/ic_account_circle_qs_muted.xml
new file mode 100644
index 0000000..afcddf1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_account_circle_qs_muted.xml
@@ -0,0 +1,37 @@
+<?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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="24dp"
+        android:height="24dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <group
+        android:scaleX="1.2"
+        android:scaleY="1.2"
+        android:pivotX="12.0"
+        android:pivotY="12.0">
+    <path
+        android:fill="@color/qs_user_detail_icon_muted"
+        android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/>
+    </group>
+</vector>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 00b3645..29d92e5 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -16,6 +16,7 @@
   ~ limitations under the License
   -->
 
+<!-- LinearLayout -->
 <com.android.systemui.qs.tiles.UserDetailItemView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:systemui="http://schemas.android.com/apk/res-auto"
@@ -39,6 +40,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textSize="14sp"
-            android:text="@string/guest_nickname"/>
+            android:textColor="@color/qs_user_detail_name" />
 
 </com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
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/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index e84300d..e3ac1c1 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -26,6 +26,23 @@
     android:fitsSystemWindows="true"
     android:descendantFocusability="afterDescendants">
 
+    <FrameLayout
+            android:id="@+id/backdrop"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            >
+        <ImageView android:id="@+id/backdrop_back"
+                   android:layout_width="match_parent"
+                   android:scaleType="centerCrop"
+                   android:layout_height="match_parent" />
+        <ImageView android:id="@+id/backdrop_front"
+                   android:layout_width="match_parent"
+                   android:layout_height="match_parent"
+                   android:scaleType="centerCrop"
+                   android:visibility="invisible" />
+    </FrameLayout>
+
     <View android:id="@+id/scrim_behind"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 11f50ee..8b1c2b7 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -78,4 +78,11 @@
         android:orientation="vertical"
         android:paddingTop="3dp" />
 
+    <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" />
 </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 adab243..a1064fd 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -38,6 +38,7 @@
     <color name="system_secondary_color">#ff384248</color>
     <color name="system_accent_color">#ff80CBC4</color><!-- deep teal 200 -->
     <color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
+    <color name="qs_text">#FFFFFFFF</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
     <color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
@@ -46,6 +47,7 @@
     <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
     <color name="data_usage_graph_warning">#FFFFFFFF</color>
     <color name="status_bar_clock_color">#FFFFFFFF</color>
+    <color name="qs_user_detail_icon_muted">#4DB0BEC5</color> <!-- 30% blue grey 200-->
 
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 751c889..46057af 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -416,17 +416,17 @@
     <string name="accessibility_quick_settings_less_time">Less time</string>
 
     <!-- Title of dialog shown when 2G-3G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
-    <string name="data_usage_disabled_dialog_3g_title">2G-3G data disabled</string>
+    <string name="data_usage_disabled_dialog_3g_title">2G-3G data is off</string>
     <!-- Title of dialog shown when 4G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
-    <string name="data_usage_disabled_dialog_4g_title">4G data disabled</string>
+    <string name="data_usage_disabled_dialog_4g_title">4G data is off</string>
     <!-- Title of dialog shown when mobile data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
-    <string name="data_usage_disabled_dialog_mobile_title">Mobile data disabled</string>
+    <string name="data_usage_disabled_dialog_mobile_title">Cellular data is off</string>
     <!-- Title of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
-    <string name="data_usage_disabled_dialog_title">Data disabled</string>
+    <string name="data_usage_disabled_dialog_title">Data is off</string>
     <!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] -->
-    <string name="data_usage_disabled_dialog">You\'ve reached the specified data usage limit.\n\nIf you re-enable data, you may be charged by the operator.</string>
+    <string name="data_usage_disabled_dialog">Your device turned off data because it reached the limit you set.\n\nTurning it back on may lead to charges from your carrier.</string>
     <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
-    <string name="data_usage_disabled_dialog_enable">Re-enable data</string>
+    <string name="data_usage_disabled_dialog_enable">Turn on data</string>
 
     <!-- Text to display underneath the graphical signal strength meter when
          no connection is available. [CHAR LIMIT=20] -->
@@ -617,8 +617,8 @@
     <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
     <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
 
-    <!-- Zen mode: No interruptions title, with a warning about alarms and timers. [CHAR LIMIT=60] -->
-    <string name="zen_no_interruptions_with_warning">No interruptions, including alarms and timers</string>
+    <!-- Zen mode: No interruptions title, with a warning about alarms. [CHAR LIMIT=60] -->
+    <string name="zen_no_interruptions_with_warning">No interruptions, including alarms</string>
 
     <!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
     <string name="zen_no_interruptions">No interruptions</string>
@@ -626,6 +626,15 @@
     <!-- Zen mode: Only important interruptions. [CHAR LIMIT=40] -->
     <string name="zen_important_interruptions">Priority interruptions only</string>
 
+    <!-- Zen mode: Next alarm information - just a time. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_information_time">Your next alarm is at <xliff:g id="alarm_time" example="5:00 PM">%s</xliff:g></string>
+
+    <!-- Zen mode: Next alarm information - day and time. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_information_day_time">Your next alarm is <xliff:g id="alarm_day_and_time" example="Fri 5:00 PM">%s</xliff:g></string>
+
+    <!-- Zen mode: Next alarm warning. [CHAR LIMIT=40] -->
+    <string name="zen_alarm_warning">You won\'t hear your alarm at <xliff:g id="alarm_time" example="5:00 PM">%s</xliff:g></string>
+
     <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
     <string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
 
@@ -694,4 +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 708d3e8..5cc987a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -154,7 +154,7 @@
 
     <style name="TextAppearance.QS">
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">#ffffff</item>
+        <item name="android:textColor">@color/qs_text</item>
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
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/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index ec5f28c..67eef56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -81,15 +81,9 @@
             UserSwitcherController.UserRecord item = getItem(position);
             UserDetailItemView v = UserDetailItemView.convertOrInflate(
                     mContext, convertView, parent);
-            String name;
-            if (item.isGuest) {
-                name = mContext.getString(
-                        item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
-            } else {
-                name = item.info.name;
-            }
+            String name = getName(mContext, item);
             if (item.picture == null) {
-                v.bind(name, mContext.getDrawable(R.drawable.ic_account_circle));
+                v.bind(name, mContext.getDrawable(R.drawable.ic_account_circle_qs));
             } else {
                 v.bind(name, item.picture);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 607e155..bd5df28 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.misc;
 
+import android.content.Intent;
 import android.graphics.Color;
 import android.graphics.Rect;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -93,4 +94,10 @@
             throws IllegalAccessException, InvocationTargetException {
         sPropertyMethod.invoke(null, property, value);
     }
+
+    /** Returns whether the specified intent is a document. */
+    public static boolean isDocument(Intent intent) {
+        int flags = intent.getFlags();
+        return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
index 5f4fabe..bb4dc76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
@@ -28,20 +28,20 @@
 public class KeyStoreLruCache<V> {
     // We keep a set of keys that are associated with the LRU cache, so that we can find out
     // information about the Task that was previously in the cache.
-    HashMap<Task.TaskKey, Task.TaskKey> mKeys = new HashMap<Task.TaskKey, Task.TaskKey>();
+    HashMap<Integer, Task.TaskKey> mTaskKeys = new HashMap<Integer, Task.TaskKey>();
     // The cache implementation
-    LruCache<Task.TaskKey, V> mCache;
+    LruCache<Integer, V> mCache;
 
     public KeyStoreLruCache(int cacheSize) {
-        mCache = new LruCache<Task.TaskKey, V>(cacheSize) {
+        mCache = new LruCache<Integer, V>(cacheSize) {
             @Override
-            protected int sizeOf(Task.TaskKey t, V v) {
+            protected int sizeOf(Integer taskId, V v) {
                 return computeSize(v);
             }
 
             @Override
-            protected void entryRemoved(boolean evicted, Task.TaskKey key, V oldV, V newV) {
-                mKeys.remove(key);
+            protected void entryRemoved(boolean evicted, Integer taskId, V oldV, V newV) {
+                mTaskKeys.remove(taskId);
             }
         };
     }
@@ -53,46 +53,41 @@
 
     /** Gets a specific entry in the cache. */
     final V get(Task.TaskKey key) {
-        return mCache.get(key);
+        return mCache.get(key.id);
     }
 
     /**
-     * Returns the value only if the last active time of the key currently in the lru cache is
-     * greater than or equal to the last active time of the key specified.
+     * Returns the value only if the Task has not updated since the last time it was in the cache.
      */
-    final V getCheckLastActiveTime(Task.TaskKey key) {
-        Task.TaskKey lruKey = mKeys.get(key);
-        if (lruKey != null && (lruKey.lastActiveTime < key.lastActiveTime)) {
-            // The task has changed (been made active since the last time it was put into the
+    final V getAndInvalidateIfModified(Task.TaskKey key) {
+        Task.TaskKey lastKey = mTaskKeys.get(key.id);
+        if (lastKey != null && (lastKey.lastActiveTime < key.lastActiveTime)) {
+            // The task has updated (been made active since the last time it was put into the
             // LRU cache) so invalidate that item in the cache
-            remove(lruKey);
+            remove(key);
             return null;
         }
         // Either the task does not exist in the cache, or the last active time is the same as
-        // the key specified
-        return mCache.get(key);
+        // the key specified, so return what is in the cache
+        return mCache.get(key.id);
     }
 
     /** Puts an entry in the cache for a specific key. */
     final void put(Task.TaskKey key, V value) {
-        mCache.put(key, value);
-        if (mKeys.containsKey(key)) {
-            mKeys.get(key).updateLastActiveTime(key.lastActiveTime);
-        } else {
-            mKeys.put(key, key);
-        }
+        mCache.put(key.id, value);
+        mTaskKeys.put(key.id, key);
     }
 
     /** Removes a cache entry for a specific key. */
     final void remove(Task.TaskKey key) {
-        mCache.remove(key);
-        mKeys.remove(key);
+        mCache.remove(key.id);
+        mTaskKeys.remove(key.id);
     }
 
     /** Removes all the entries in the cache. */
     final void evictAll() {
         mCache.evictAll();
-        mKeys.clear();
+        mTaskKeys.clear();
     }
 
     /** Returns the size of the cache. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 71979c4f7..adf0794 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -31,6 +31,7 @@
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -42,6 +43,18 @@
     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
 
     /** Adds a new task to the load queue */
+    void addTasks(Collection<Task> tasks) {
+        for (Task t : tasks) {
+            if (!mQueue.contains(t)) {
+                mQueue.add(t);
+            }
+        }
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /** Adds a new task to the load queue */
     void addTask(Task t) {
         if (!mQueue.contains(t)) {
             mQueue.add(t);
@@ -153,30 +166,30 @@
                 // Load the next item from the queue
                 final Task t = mLoadQueue.nextTask();
                 if (t != null) {
-                    Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
-                    Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
+                    Drawable cachedIcon = mApplicationIconCache.get(t.key);
+                    Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
                     // Load the application icon if it is stale or we haven't cached one yet
                     if (cachedIcon == null) {
-                        Drawable icon = null;
                         ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
                                 t.userId);
                         if (info != null) {
-                            icon = ssp.getActivityIcon(info, t.userId);
+                            cachedIcon = ssp.getActivityIcon(info, t.userId);
                         }
-                        // If we can't load the icon, then set the default application icon into the
-                        // cache.  This will remain until the task's last active time is updated.
-                        cachedIcon = icon != null ? icon : mDefaultApplicationIcon;
+                        if (cachedIcon == null) {
+                            cachedIcon = mDefaultApplicationIcon;
+                        }
+                        // At this point, even if we can't load the icon, we will set the default
+                        // icon.
                         mApplicationIconCache.put(t.key, cachedIcon);
                     }
                     // Load the thumbnail if it is stale or we haven't cached one yet
                     if (cachedThumbnail == null) {
-                        Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
-                        if (thumbnail != null) {
-                            thumbnail.setHasAlpha(false);
+                        cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
+                        if (cachedThumbnail != null) {
+                            cachedThumbnail.setHasAlpha(false);
+                        } else {
+                            cachedThumbnail = mDefaultThumbnail;
                         }
-                        // Even if we can't load the icon, we set the default thumbnail into the
-                        // cache.  This will remain until the task's last active time is updated.
-                        cachedThumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
                         mThumbnailCache.put(t.key, cachedThumbnail);
                     }
                     if (!mCancelled) {
@@ -281,11 +294,11 @@
         return mSystemServicesProxy;
     }
 
+    /** Gets the list of recent tasks, ordered from back to front. */
     private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
         List<ActivityManager.RecentTaskInfo> tasks =
                 ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
-
         return tasks;
     }
 
@@ -302,7 +315,7 @@
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
 
-        // Add each task to the task stack
+        // From back to front, add each task to the task stack
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = tasks.get(i);
@@ -311,7 +324,7 @@
 
             ActivityManager.TaskDescription av = t.taskDescription;
             String activityLabel = null;
-            Drawable activityIcon = null;
+            Drawable activityIcon = mDefaultApplicationIcon;
             int activityColor = config.taskBarViewDefaultBackgroundColor;
             if (av != null) {
                 activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info));
@@ -323,7 +336,6 @@
             } else {
                 activityLabel = ssp.getActivityLabel(info);
             }
-            boolean isForemostTask = (i == (taskCount - 1));
 
             // Create a new task
             Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, t.affiliatedTaskId,
@@ -333,44 +345,35 @@
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
                 // Load the icon from the cache if possible
-                task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key);
+                task.applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(task.key);
                 if (task.applicationIcon == null) {
-                    if (isForemostTask) {
-                        // We force loading the application icon for the foremost task
-                        task.applicationIcon = ssp.getActivityIcon(info, task.userId);
-                        if (task.applicationIcon == null) {
-                            task.applicationIcon = mDefaultApplicationIcon;
-                        }
-                        // Even if we can't load the icon we set the default application icon into
-                        // the cache.  This will remain until the task's last active time is updated.
+                    // Load the icon from the system
+                    task.applicationIcon = ssp.getActivityIcon(info, task.userId);
+                    if (task.applicationIcon != null) {
                         mApplicationIconCache.put(task.key, task.applicationIcon);
-                    } else {
-                        // Either the task has changed since the last active time, or it was not
-                        // previously cached, so try and load the task anew.
-                        tasksToLoad.add(task);
                     }
                 }
+                if (task.applicationIcon == null) {
+                    // Either the task has changed since the last active time, or it was not
+                    // previously cached, so try and load the task anew.
+                    tasksToLoad.add(task);
+                }
 
-                // Load the thumbnail (if possible and not the foremost task, from the cache)
-                task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key);
+                // Load the thumbnail from the cache if possible
+                task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(task.key);
                 if (task.thumbnail == null) {
-                    if (isForemostTask) {
-                        // We force loading the thumbnail icon for the foremost task
-                        task.thumbnail = ssp.getTaskThumbnail(task.key.id);
-                        if (task.thumbnail != null) {
-                            task.thumbnail.setHasAlpha(false);
-                        } else {
-                            task.thumbnail = mDefaultThumbnail;
-                        }
-                        // Even if we can't load the thumbnail we set the default thumbnail into
-                        // the cache.  This will remain until the task's last active time is updated.
+                    // Load the thumbnail from the system
+                    task.thumbnail = ssp.getTaskThumbnail(task.key.id);
+                    if (task.thumbnail != null) {
+                        task.thumbnail.setHasAlpha(false);
                         mThumbnailCache.put(task.key, task.thumbnail);
-                    } else {
-                        // Either the task has changed since the last active time, or it was not
-                        // previously cached, so try and load the task anew.
-                        tasksToLoad.add(task);
                     }
                 }
+                if (task.thumbnail == null) {
+                    // Either the task has changed since the last active time, or it was not
+                    // previously cached, so try and load the task anew.
+                    tasksToLoad.add(task);
+                }
             }
 
             // Add the task to the stack
@@ -380,13 +383,9 @@
         // Simulate the groupings that we describe
         stack.createAffiliatedGroupings();
 
-        // Start the task loader
+        // Start the task loader and add all the tasks we need to load
         mLoader.start(context);
-
-        // Add all the tasks that we are reloading
-        for (Task t : tasksToLoad) {
-            mLoadQueue.addTask(t);
-        }
+        mLoadQueue.addTasks(tasksToLoad);
 
         // Update the package monitor with the list of packages to listen for
         mPackageMonitor.setTasks(tasks);
@@ -414,18 +413,14 @@
 
     /** Acquires the task resource data directly from the pool. */
     public void loadTaskData(Task t) {
-        Drawable applicationIcon = mApplicationIconCache.get(t.key);
-        Bitmap thumbnail = mThumbnailCache.get(t.key);
+        Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
+        Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
 
-        boolean requiresLoad = false;
-        if (applicationIcon == null) {
-            applicationIcon = mDefaultApplicationIcon;
-            requiresLoad = true;
-        }
-        if (thumbnail == null) {
-            thumbnail = mLoadingThumbnail;
-            requiresLoad = true;
-        }
+        // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
+        // use the default assets in their place until they load
+        boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
+        applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
+        thumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
         if (requiresLoad) {
             mLoadQueue.addTask(t);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1670735..2473352 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -51,10 +51,6 @@
             this.lastActiveTime = lastActiveTime;
         }
 
-        public void updateLastActiveTime(long lastActiveTime) {
-            this.lastActiveTime = lastActiveTime;
-        }
-
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof TaskKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 78a99e0..e1c652e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -38,6 +38,7 @@
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.SpaceNode;
@@ -458,7 +459,7 @@
                     Intent i = new Intent(task.key.baseIntent);
                     i.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                             | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-                    if ((i.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == 0) {
+                    if (!Utilities.isDocument(i)) {
                         i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     }
                     try {
@@ -510,11 +511,8 @@
         loader.deleteTaskData(t, false);
 
         // Remove the old task from activity manager
-        int flags = t.key.baseIntent.getFlags();
-        boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
-                Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
         RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
-                isDocument);
+                Utilities.isDocument(t.key.baseIntent));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 0b35f59..8b86258 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -628,6 +628,7 @@
     public void computeScroll() {
         if (mScroller.computeScrollOffset()) {
             setStackScroll(mScroller.getCurrY());
+            invalidate();
 
             // If we just finished scrolling, then disable the hw layers
             if (mScroller.isFinished()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 56ea359..0a288d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -581,7 +581,7 @@
         }
     }
 
-    private boolean isMediaNotification(NotificationData.Entry entry) {
+    public boolean isMediaNotification(NotificationData.Entry entry) {
         // TODO: confirm that there's a valid media key
         return entry.expandedBig != null &&
                entry.expandedBig.findViewById(com.android.internal.R.id.media_action_area) != null;
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 0fa4810..72c12d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -45,15 +45,23 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -91,6 +99,7 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -140,6 +149,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 
 public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         DragDownHelper.OnDragDownListener, ActivityStarter {
@@ -148,17 +158,19 @@
     public static final boolean SPEW = false;
     public static final boolean DUMPTRUCK = true; // extra dumpsys info
     public static final boolean DEBUG_GESTURES = false;
+    public static final boolean DEBUG_MEDIA = false;
+    public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
 
     public static final boolean DEBUG_WINDOW_STATE = false;
 
-    public static final boolean SETTINGS_DRAG_SHORTCUT = true;
-
     // additional instrumentation for testing purposes; intended to be left on during development
     public static final boolean CHATTY = DEBUG;
 
     public static final String ACTION_STATUSBAR_START
             = "com.android.internal.policy.statusbar.START";
 
+    public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
+
     private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
     private static final int MSG_CLOSE_PANELS = 1001;
     private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
@@ -378,6 +390,38 @@
     private Interpolator mAlphaIn = new PathInterpolator(0f, 0.2f, 1f, 1f);
     private Interpolator mAlphaOut = new PathInterpolator(0f, 0f, 0.8f, 1f);
 
+    private FrameLayout mBackdrop;
+    private ImageView mBackdropFront, mBackdropBack;
+
+    private MediaSessionManager mMediaSessionManager;
+    private MediaController mMediaController;
+    private String mMediaNotificationKey;
+    private MediaMetadata mMediaMetadata;
+    private MediaController.Callback mMediaListener
+            = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            super.onPlaybackStateChanged(state);
+            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            super.onMetadataChanged(metadata);
+            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
+            mMediaMetadata = metadata;
+            updateMediaMetaData(true);
+        }
+    };
+
+    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
+            new OnChildLocationsChangedListener() {
+        @Override
+        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
+            userActivity();
+        }
+    };
+
     private int mDisabledUnmodified;
 
     /** Keys of notifications currently visible to the user. */
@@ -480,6 +524,11 @@
         updateDisplaySize();
         super.start(); // calls createAndAddWindows()
 
+        mMediaSessionManager
+                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
+        // in session state
+
         addNavigationBar();
 
         // Lastly, call to the icon policy to install/update all the icons.
@@ -500,6 +549,8 @@
 
         mDozeServiceHost = new DozeServiceHost();
         putComponent(DozeService.Host.class, mDozeServiceHost);
+
+        setControllerUsers();
     }
 
     // ================================================================================
@@ -710,6 +761,10 @@
             mHeader.setQSPanel(mQSPanel);
         }
 
+        mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop);
+        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
+        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
+
         // User info. Trigger first load.
         mHeader.setUserInfoController(mUserInfoController);
         mUserInfoController.reloadUserInfo();
@@ -725,6 +780,9 @@
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_SCREEN_ON);
+        if (DEBUG_MEDIA_FAKE_ARTWORK) {
+            filter.addAction("fake_artwork");
+        }
         filter.addAction(ACTION_DEMO);
         context.registerReceiver(mBroadcastReceiver, filter);
 
@@ -1409,9 +1467,240 @@
                 .start();
         }
 
+        findAndUpdateMediaNotifications();
+
         updateCarrierLabelVisibility(false);
     }
 
+    public void findAndUpdateMediaNotifications() {
+        boolean metaDataChanged = false;
+
+        synchronized (mNotificationData) {
+            final int N = mNotificationData.size();
+            Entry mediaNotification = null;
+            MediaController controller = null;
+            for (int i=0; i<N; i++) {
+                final Entry entry = mNotificationData.get(i);
+                if (isMediaNotification(entry)) {
+                    final MediaSession.Token token = entry.notification.getNotification().extras
+                            .getParcelable(Notification.EXTRA_MEDIA_SESSION);
+                    if (token != null) {
+                        controller = new MediaController(token);
+                        if (controller != null) {
+                            // we've got a live one, here
+                            mediaNotification = entry;
+                        }
+                    }
+                }
+            }
+
+            if (mediaNotification == null) {
+                // Still nothing? OK, let's just look for live media sessions and see if they match
+                // one of our notifications. This will catch apps that aren't (yet!) using media
+                // notifications.
+
+                if (mMediaSessionManager != null) {
+                    final List<MediaController> sessions
+                            = mMediaSessionManager.getActiveSessionsForUser(
+                                    null,
+                                    UserHandle.USER_ALL);
+
+                    for (MediaController aController : sessions) {
+                        if (aController == null) continue;
+                        final PlaybackState state = aController.getPlaybackState();
+                        if (state == null) continue;
+                        switch (state.getState()) {
+                            case PlaybackState.STATE_STOPPED:
+                            case PlaybackState.STATE_ERROR:
+                                continue;
+                            default:
+                                // now to see if we have one like this
+                                final String pkg = aController.getSessionInfo().getPackageName();
+
+                                for (int i = 0; i < N; i++) {
+                                    final Entry entry = mNotificationData.get(i);
+                                    if (entry.notification.getPackageName().equals(pkg)) {
+                                        if (DEBUG_MEDIA) {
+                                            Log.v(TAG, "DEBUG_MEDIA: found controller matching "
+                                                + entry.notification.getKey());
+                                        }
+                                        controller = aController;
+                                        mediaNotification = entry;
+                                        break;
+                                    }
+                                }
+                        }
+                    }
+                }
+            }
+
+            if (controller != mMediaController) {
+                // We have a new media session
+
+                if (mMediaController != null) {
+                    // something old was playing
+                    Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
+                            + mMediaController);
+                    mMediaController.removeCallback(mMediaListener);
+                }
+                mMediaController = controller;
+
+                if (mMediaController != null) {
+                    mMediaController.addCallback(mMediaListener);
+                    mMediaMetadata = mMediaController.getMetadata();
+                    if (DEBUG_MEDIA) {
+                        Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
+                                + mMediaMetadata);
+                    }
+
+                    final String notificationKey = mediaNotification == null
+                            ? null
+                            : mediaNotification.notification.getKey();
+
+                    if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
+                        // we have a new notification!
+                        if (DEBUG_MEDIA) {
+                            Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+                                    + notificationKey + " controller=" + controller);
+                        }
+                        mMediaNotificationKey = notificationKey;
+                    }
+                } else {
+                    mMediaMetadata = null;
+                    mMediaNotificationKey = null;
+                }
+
+                metaDataChanged = true;
+            } else {
+                // Media session unchanged
+
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
+                }
+            }
+        }
+
+        updateMediaMetaData(metaDataChanged);
+    }
+
+    private void removeAndRecycleImageViewDrawable(ImageView iv) {
+        Bitmap oldBitmap = null;
+        final Drawable drawable = iv.getDrawable();
+        if (drawable != null && drawable instanceof BitmapDrawable) {
+            oldBitmap = ((BitmapDrawable) drawable).getBitmap();
+        }
+        iv.animate().cancel();
+        iv.setImageDrawable(null);
+        if (oldBitmap != null) {
+            if (DEBUG_MEDIA) {
+                Log.v(TAG, "DEBUG_MEDIA: recycling bitmap " + oldBitmap + " from ImageView " + iv);
+            }
+            oldBitmap.recycle();
+        }
+    }
+
+    /**
+     * Hide the album artwork that is fading out and release its memory.
+     */
+    private Runnable mHideBackdropFront = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG_MEDIA) {
+                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
+            }
+            mBackdropFront.setVisibility(View.INVISIBLE);
+            removeAndRecycleImageViewDrawable(mBackdropFront);
+        }
+    };
+
+    /**
+     * Refresh or remove lockscreen artwork from media metadata.
+     */
+    public void updateMediaMetaData(boolean metaDataChanged) {
+        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
+
+        if (mBackdrop == null) return; // called too early
+
+        if (DEBUG_MEDIA) {
+            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
+                + " metadata=" + mMediaMetadata
+                + " metaDataChanged=" + metaDataChanged
+                + " state=" + mState);
+        }
+
+        Bitmap artworkBitmap = null;
+        if (mMediaMetadata != null) {
+            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+            if (artworkBitmap == null) {
+                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+                // might still be null
+            }
+        }
+
+        final boolean hasArtwork = artworkBitmap != null;
+
+        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
+                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
+            // time to show some art!
+            if (mBackdrop.getVisibility() != View.VISIBLE) {
+                mBackdrop.setVisibility(View.VISIBLE);
+                mBackdrop.animate().alpha(1f);
+                metaDataChanged = true;
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
+                }
+            }
+            if (metaDataChanged) {
+                if (mBackdropBack.getDrawable() != null) {
+                    mBackdropFront.setImageDrawable(mBackdropBack.getDrawable());
+                    mBackdropFront.setAlpha(1f);
+                    mBackdropFront.setVisibility(View.VISIBLE);
+                } else {
+                    mBackdropFront.setVisibility(View.INVISIBLE);
+                }
+
+                if (DEBUG_MEDIA_FAKE_ARTWORK) {
+                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
+                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
+                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
+                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
+                } else {
+                    mBackdropBack.setImageBitmap(artworkBitmap);
+                }
+
+                if (mBackdropFront.getVisibility() == View.VISIBLE) {
+                    if (DEBUG_MEDIA) {
+                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
+                                + mBackdropFront.getDrawable()
+                                + " to "
+                                + mBackdropBack.getDrawable());
+                    }
+                    mBackdropFront.animate().withLayer()
+                            .setDuration(250)
+                            .alpha(0f).withEndAction(mHideBackdropFront);
+                }
+            }
+        } else {
+            // need to hide the album art, either because we are unlocked or because
+            // the metadata isn't there to support it
+            if (mBackdrop.getVisibility() != View.GONE) {
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
+                }
+                mBackdrop.animate().withLayer()
+                        .alpha(0f).withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        mBackdrop.setVisibility(View.GONE);
+                        mBackdropFront.animate().cancel();
+                        mBackdropBack.animate().cancel();
+                        mHandler.post(mHideBackdropFront);
+                    }
+                });
+            }
+        }
+    }
+
     public void showClock(boolean show) {
         if (mStatusBarView == null) return;
         View clock = mStatusBarView.findViewById(R.id.clock);
@@ -2272,6 +2561,23 @@
             mNavigationBarView.dump(fd, pw, args);
         }
 
+        pw.print("  mMediaSessionManager=");
+        pw.println(mMediaSessionManager);
+        pw.print("  mMediaNotificationKey=");
+        pw.println(mMediaNotificationKey);
+        pw.print("  mMediaController=");
+        pw.print(mMediaController);
+        if (mMediaController != null) {
+            pw.print(" state=" + mMediaController.getPlaybackState());
+        }
+        pw.println();
+        pw.print("  mMediaMetadata=");
+        pw.print(mMediaMetadata);
+        if (mMediaMetadata != null) {
+            pw.print(" title=" + mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+        }
+        pw.println();
+
         pw.println("  Panels: ");
         if (mNotificationPanel != null) {
             pw.println("    mNotificationPanel=" +
@@ -2453,6 +2759,10 @@
                         }
                     }
                 }
+            } else if ("fake_artwork".equals(action)) {
+                if (DEBUG_MEDIA_FAKE_ARTWORK) {
+                    updateMediaMetaData(true);
+                }
             }
         }
     };
@@ -2489,6 +2799,13 @@
         animateCollapsePanels();
         updateNotifications();
         resetUserSetupObserver();
+        setControllerUsers();
+    }
+
+    private void setControllerUsers() {
+        if (mZenModeController != null) {
+            mZenModeController.setUserId(mCurrentUserId);
+        }
     }
 
     private void resetUserSetupObserver() {
@@ -2890,6 +3207,7 @@
         updateNotifications();
         checkBarModes();
         updateCarrierLabelVisibility(false);
+        updateMediaMetaData(false);
     }
 
     private void updateDozingState() {
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 dc06ec2..574d536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -285,9 +285,6 @@
         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);
         }
@@ -440,12 +437,13 @@
     public void attachSystemIcons(LinearLayout systemIcons) {
         mSystemIconsContainer.addView(systemIcons);
         mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
+        mStatusIcons.addOnLayoutChangeListener(mStatusIconsChanged);
         mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
     }
 
     public void onSystemIconsDetached() {
         if (mStatusIcons != null) {
-            mStatusIcons.setVisibility(View.VISIBLE);
+            mStatusIcons.removeOnLayoutChangeListener(mStatusIconsChanged);
         }
         if (mSignalCluster != null) {
             mSignalCluster.setVisibility(View.VISIBLE);
@@ -530,6 +528,23 @@
         // here.
     }
 
+    private final OnLayoutChangeListener mStatusIconsChanged = new OnLayoutChangeListener() {
+        private final Rect mClipBounds = new Rect();
+
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right,
+                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);
+            }
+        }
+    };
+
     private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
         @Override
         public void onToggleStateChanged(final boolean state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 6f2642a..93561aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -216,4 +216,17 @@
             super.invalidateDrawable(dr);
         }
     }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return who == mDrawable || super.verifyDrawable(who);
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+        if (mDrawable != null && mDrawable.isStateful()) {
+            mDrawable.setState(getDrawableState());
+        }
+    }
 }
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 4640067..7cc8ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -30,8 +30,10 @@
 import android.graphics.Bitmap;
 import android.os.AsyncTask;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManagerGlobal;
@@ -64,15 +66,37 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mReceiver, filter);
-        refreshUsers();
+        filter.addAction(Intent.ACTION_USER_STOPPING);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
+                null /* permission */, null /* scheduler */);
+        refreshUsers(UserHandle.USER_NULL);
     }
 
-    private void refreshUsers() {
-        new AsyncTask<Void, Void, ArrayList<UserRecord>>() {
+    /**
+     * Refreshes users from UserManager.
+     *
+     * The pictures are only loaded if they have not been loaded yet.
+     *
+     * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
+     */
+    private void refreshUsers(int forcePictureLoadForId) {
 
+        SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
+        final int N = mUsers.size();
+        for (int i = 0; i < N; i++) {
+            UserRecord r = mUsers.get(i);
+            if (r == null || r.info == null
+                    || r.info.id == forcePictureLoadForId || r.picture == null) {
+                continue;
+            }
+            bitmaps.put(r.info.id, r.picture);
+        }
+
+        new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
+            @SuppressWarnings("unchecked")
             @Override
-            protected ArrayList<UserRecord> doInBackground(Void... params) {
+            protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
+                final SparseArray<Bitmap> bitmaps = params[0];
                 List<UserInfo> infos = mUserManager.getUsers(true);
                 if (infos == null) {
                     return null;
@@ -87,8 +111,11 @@
                         guestRecord = new UserRecord(info, null /* picture */,
                                 true /* isGuest */, isCurrent);
                     } else if (!info.isManagedProfile()) {
-                        records.add(new UserRecord(info, mUserManager.getUserIcon(info.id),
-                                false /* isGuest */, isCurrent));
+                        Bitmap picture = bitmaps.get(info.id);
+                        if (picture == null) {
+                            picture = mUserManager.getUserIcon(info.id);
+                        }
+                        records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent));
                     }
                 }
 
@@ -109,7 +136,7 @@
                     notifyAdapters();
                 }
             }
-        }.execute((Void[])null);
+        }.execute((SparseArray)bitmaps);
     }
 
     private void notifyAdapters() {
@@ -134,9 +161,16 @@
         }
 
         if (ActivityManager.getCurrentUser() == id) {
+            if (record.isGuest) {
+                exitGuest(id);
+            }
             return;
         }
 
+        switchToUserId(id);
+    }
+
+    private void switchToUserId(int id) {
         try {
             WindowManagerGlobal.getWindowManagerService().lockNow(null);
             ActivityManagerNative.getDefault().switchUser(id);
@@ -145,6 +179,12 @@
         }
     }
 
+    private void exitGuest(int id) {
+        // TODO: show confirmation dialog
+        switchToUserId(UserHandle.USER_OWNER);
+        mUserManager.removeUser(id);
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -153,15 +193,20 @@
                 final int N = mUsers.size();
                 for (int i = 0; i < N; i++) {
                     UserRecord record = mUsers.get(i);
+                    if (record.info == null) continue;
                     boolean shouldBeCurrent = record.info.id == currentId;
                     if (record.isCurrent != shouldBeCurrent) {
                         mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
                     }
                 }
                 notifyAdapters();
-            } else {
-                refreshUsers();
             }
+            int forcePictureLoadForId = UserHandle.USER_NULL;
+            if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
+                forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL);
+            }
+            refreshUsers(forcePictureLoadForId);
         }
     };
 
@@ -195,12 +240,25 @@
 
         @Override
         public long getItemId(int position) {
-            return mController.mUsers.get(position).info.id;
+            return position;
         }
 
         public void switchTo(UserRecord record) {
             mController.switchTo(record);
         }
+
+        public String getName(Context context, UserRecord item) {
+            if (item.isGuest) {
+                if (item.isCurrent) {
+                    return context.getString(R.string.guest_exit_guest);
+                } else {
+                    return context.getString(
+                            item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
+                }
+            } else {
+                return item.info.name;
+            }
+        }
     }
 
     public static final class UserRecord {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 61902a2f..2e97d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -27,11 +27,13 @@
     void requestConditions(boolean request);
     void setExitConditionId(Uri exitConditionId);
     Uri getExitConditionId();
-    boolean hasNextAlarm();
+    long getNextAlarm();
+    void setUserId(int userId);
 
     public static class Callback {
         public void onZenChanged(int zen) {}
         public void onExitConditionChanged(Uri exitConditionId) {}
         public void onConditionsChanged(Condition[] conditions) {}
+        public void onNextAlarmChanged() {}
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 7703966..a3cdd41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,19 +16,25 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.AlarmClockInfo;
+import android.app.AlarmManager;
 import android.app.INotificationManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.ZenModeConfig;
+import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.qs.GlobalSetting;
 
 import java.util.ArrayList;
@@ -36,8 +42,8 @@
 
 /** Platform implementation of the zen mode controller. **/
 public class ZenModeControllerImpl implements ZenModeController {
-    private static final String TAG = "ZenModeControllerImpl";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "ZenModeController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final Context mContext;
@@ -45,13 +51,14 @@
     private final GlobalSetting mConfigSetting;
     private final INotificationManager mNoMan;
     private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
-    private final LockPatternUtils mUtils;
+    private final AlarmManager mAlarmManager;
 
+    private int mUserId;
     private boolean mRequesting;
+    private boolean mRegistered;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
         mContext = context;
-        mUtils = new LockPatternUtils(mContext);
         mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
             @Override
             protected void handleValueChanged(int value) {
@@ -68,6 +75,7 @@
         mConfigSetting.setListening(true);
         mNoMan = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
     }
 
     @Override
@@ -126,8 +134,26 @@
     }
 
     @Override
-    public boolean hasNextAlarm() {
-        return mUtils.getNextAlarm() != null;
+    public long getNextAlarm() {
+        final AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId);
+        return info != null ? info.getTriggerTime() : 0;
+    }
+
+    @Override
+    public void setUserId(int userId) {
+        mUserId = userId;
+        if (mRegistered) {
+            mContext.unregisterReceiver(mReceiver);
+        }
+        mContext.registerReceiverAsUser(mReceiver, new UserHandle(mUserId),
+                new IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED), null, null);
+        mRegistered = true;
+    }
+
+    private void fireNextAlarmChanged() {
+        for (Callback cb : mCallbacks) {
+            cb.onNextAlarmChanged();
+        }
     }
 
     private void fireZenChanged(int zen) {
@@ -169,4 +195,13 @@
             updateConditions(conditions);
         }
     };
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(intent.getAction())) {
+                fireNextAlarmChanged();
+            }
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index cf04219..33cf3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -30,6 +30,8 @@
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
+import android.text.format.DateFormat;
+import android.text.format.Time;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -46,7 +48,10 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
 import java.util.Objects;
 
 public class ZenModePanel extends LinearLayout {
@@ -75,6 +80,8 @@
     private final H mHandler = new H();
     private final Favorites mFavorites;
     private final Interpolator mFastOutSlowInInterpolator;
+    private final int mTextColor;
+    private final int mAccentColor;
 
     private char mLogTag = '?';
     private String mTag;
@@ -85,6 +92,7 @@
     private TextView mZenSubheadExpanded;
     private View mMoreSettings;
     private LinearLayout mZenConditions;
+    private TextView mAlarmWarning;
 
     private Callback mCallback;
     private ZenModeController mController;
@@ -92,8 +100,10 @@
     private Uri mExitConditionId;
     private int mBucketIndex = -1;
     private boolean mExpanded;
-    private int mAttachedZen;
+    private int mSessionZen;
+    private Uri mSessionExitConditionId;
     private String mExitConditionText;
+    private long mNextAlarm;
 
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -102,6 +112,8 @@
         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);
         updateTag();
         if (DEBUG) Log.d(mTag, "new ZenModePanel");
     }
@@ -144,14 +156,17 @@
         });
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+        mAlarmWarning = (TextView) findViewById(R.id.zen_alarm_warning);
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (DEBUG) Log.d(mTag, "onAttachedToWindow");
-        mAttachedZen = getSelectedZen(-1);
+        mSessionZen = getSelectedZen(-1);
+        mSessionExitConditionId = mExitConditionId;
         refreshExitConditionText();
+        refreshNextAlarm();
         updateWidgets();
     }
 
@@ -159,7 +174,8 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
-        mAttachedZen = -1;
+        mSessionZen = -1;
+        mSessionExitConditionId = null;
         setExpanded(false);
     }
 
@@ -202,7 +218,7 @@
         updateTag();
         setExitConditionId(mController.getExitConditionId());
         refreshExitConditionText();
-        mAttachedZen = getSelectedZen(-1);
+        mSessionZen = getSelectedZen(-1);
         handleUpdateZen(mController.getZen());
         if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
         mZenConditions.removeAllViews();
@@ -213,6 +229,7 @@
         if (Objects.equals(mExitConditionId, exitConditionId)) return;
         mExitConditionId = exitConditionId;
         refreshExitConditionText();
+        updateWidgets();
     }
 
     private void refreshExitConditionText() {
@@ -248,9 +265,9 @@
     }
 
     private void handleUpdateZen(int zen) {
-        if (mAttachedZen != -1 && mAttachedZen != zen) {
+        if (mSessionZen != -1 && mSessionZen != zen) {
             setExpanded(zen != Global.ZEN_MODE_OFF);
-            mAttachedZen = zen;
+            mSessionZen = zen;
         }
         mZenButtons.setSelectedValue(zen);
         updateWidgets();
@@ -267,12 +284,34 @@
         final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
         final boolean foreverSelected = mExitConditionId == null;
+        final boolean hasNextAlarm = mNextAlarm != 0;
 
         mZenSubhead.setVisibility(!zenOff && (mExpanded || !foreverSelected) ? VISIBLE : GONE);
         mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
         mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
         mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
         mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
+        mAlarmWarning.setVisibility(zenNone && mExpanded && hasNextAlarm ? VISIBLE : GONE);
+
+        if (zenNone && mExpanded && hasNextAlarm) {
+            final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId);
+            final long now = System.currentTimeMillis();
+            final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay;
+            final String skeleton = (alarmToday ? "" : "E")
+                    + (DateFormat.is24HourFormat(mContext) ? "Hm" : "hma");
+            final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+            final String alarm = new SimpleDateFormat(pattern).format(new Date(mNextAlarm));
+            final boolean isWarning = exitTime > 0 && mNextAlarm > now && mNextAlarm < exitTime;
+            if (isWarning) {
+                mAlarmWarning.setText(mContext.getString(R.string.zen_alarm_warning, alarm));
+                mAlarmWarning.setTextColor(mAccentColor);
+            } else {
+                mAlarmWarning.setText(mContext.getString(alarmToday
+                        ? R.string.zen_alarm_information_time
+                        : R.string.zen_alarm_information_day_time, alarm));
+                mAlarmWarning.setTextColor(mTextColor);
+            }
+        }
 
         if (zenNone) {
             mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);
@@ -283,6 +322,12 @@
         }
     }
 
+    private static Time time(long millis) {
+        final Time t = new Time();
+        t.set(millis);
+        return t;
+    }
+
     private Condition parseExistingTimeCondition(Uri conditionId) {
         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
         if (time == 0) return null;
@@ -308,6 +353,15 @@
                 Condition.FLAG_RELEVANT_NOW);
     }
 
+    private void refreshNextAlarm() {
+        mNextAlarm = mController.getNextAlarm();
+    }
+
+    private void handleNextAlarmChanged() {
+        refreshNextAlarm();
+        updateWidgets();
+    }
+
     private void handleUpdateConditions(Condition[] conditions) {
         final int newCount = conditions == null ? 0 : conditions.length;
         if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
@@ -328,6 +382,8 @@
         // are we left without anything selected?  if so, set a default
         for (int i = 0; i < mZenConditions.getChildCount(); i++) {
             if (getConditionTagAt(i).rb.isChecked()) {
+                if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
+                        + getConditionTagAt(i).conditionId);
                 return;
             }
         }
@@ -371,7 +427,7 @@
         }
         tag.conditionId = condition != null ? condition.id : null;
         tag.rb.setEnabled(enabled);
-        if (Objects.equals(tag.conditionId, mExitConditionId)) {
+        if (mSessionExitConditionId != null && mSessionExitConditionId.equals(tag.conditionId)) {
             tag.rb.setChecked(true);
         }
         tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@@ -484,6 +540,7 @@
         } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
             mFavorites.setMinuteIndex(mBucketIndex);
         }
+        mSessionExitConditionId = conditionId;
     }
 
     private void fireMoreSettings() {
@@ -518,12 +575,18 @@
         public void onExitConditionChanged(Uri exitConditionId) {
             mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
         }
+
+        @Override
+        public void onNextAlarmChanged() {
+            mHandler.sendEmptyMessage(H.NEXT_ALARM_CHANGED);
+        }
     };
 
     private final class H extends Handler {
         private static final int UPDATE_CONDITIONS = 1;
         private static final int EXIT_CONDITION_CHANGED = 2;
         private static final int UPDATE_ZEN = 3;
+        private static final int NEXT_ALARM_CHANGED = 4;
 
         private H() {
             super(Looper.getMainLooper());
@@ -532,12 +595,14 @@
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == UPDATE_CONDITIONS) {
-                handleUpdateConditions((Condition[])msg.obj);
+                handleUpdateConditions((Condition[]) msg.obj);
                 checkForDefault();
             } else if (msg.what == EXIT_CONDITION_CHANGED) {
-                handleExitConditionChanged((Uri)msg.obj);
+                handleExitConditionChanged((Uri) msg.obj);
             } else if (msg.what == UPDATE_ZEN) {
                 handleUpdateZen(msg.arg1);
+            } else if (msg.what == NEXT_ALARM_CHANGED) {
+                handleNextAlarmChanged();
             }
         }
     }
@@ -601,7 +666,6 @@
             if (value != null && mZenButtons.isShown()) {
                 if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
                 mController.setZen((Integer) value);
-                mController.setExitConditionId(null);
             }
         }
     };
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index edeb5b4..da0bc30 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1729,6 +1729,7 @@
                         return true;
                     }
                 }
+                return false;
             }
 
             case KeyEvent.KEYCODE_MENU: {
@@ -1822,6 +1823,7 @@
                         return true;
                     }
                 }
+                return false;
             }
 
             case KeyEvent.KEYCODE_MENU: {
@@ -2271,6 +2273,22 @@
         }
 
         @Override
+        public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+            if (mOutsetBottom != null) {
+                final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+                int bottom = (int) mOutsetBottom.getDimension(metrics);
+                WindowInsets newInsets = insets.replaceSystemWindowInsets(
+                        insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                        insets.getSystemWindowInsetRight(),
+                        insets.getSystemWindowInsetBottom() + bottom);
+                return super.dispatchApplyWindowInsets(newInsets);
+            } else {
+                return super.dispatchApplyWindowInsets(insets);
+            }
+        }
+
+
+        @Override
         public boolean onTouchEvent(MotionEvent event) {
             return onInterceptTouchEvent(event);
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 608aa44..a3c84c6 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1706,7 +1706,8 @@
     @Override
     public boolean isForceHiding(WindowManager.LayoutParams attrs) {
         return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
-                (isKeyguardHostWindow(attrs) && isKeyguardSecureIncludingHidden());
+                (isKeyguardHostWindow(attrs) && isKeyguardSecureIncludingHidden()) ||
+                (attrs.type == TYPE_KEYGUARD_SCRIM);
     }
 
     @Override
diff --git a/preloaded-classes b/preloaded-classes
index 4413cd3..b3fc517 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1208,7 +1208,6 @@
 android.webkit.WebViewClient
 android.webkit.WebViewDatabase
 android.webkit.WebViewFactory
-android.webkit.WebViewFactory$Preloader
 android.webkit.WebViewFactoryProvider
 android.webkit.WebViewFactoryProvider$Statics
 android.webkit.WebViewProvider
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e8e2813..f54f798 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -233,6 +233,7 @@
     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
     static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+    static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
 
     // User confirmation timeout for a full backup/restore operation.  It's this long in
     // order to give them time to enter the backup password.
@@ -6309,6 +6310,7 @@
         RUNNING_QUEUE,
         RESTORE_KEYVALUE,
         RESTORE_FULL,
+        RESTORE_FINISHED,
         FINAL
     }
 
@@ -6343,6 +6345,9 @@
         // Our bookkeeping about the ancestral dataset
         private PackageManagerBackupAgent mPmAgent;
 
+        // Currently-bound backup agent for restore + restoreFinished purposes
+        private IBackupAgent mAgent;
+
         // What sort of restore we're doing now
         private RestoreDescription mRestoreDescription;
 
@@ -6441,6 +6446,13 @@
                     // this one is always valid too
                 }
             }
+
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
+                for (PackageInfo info : mAcceptSet) {
+                    Slog.v(TAG, "   " + info.packageName);
+                }
+            }
         }
 
         private String[] packagesToNames(List<PackageInfo> apps) {
@@ -6473,6 +6485,10 @@
                     restoreFull();
                     break;
 
+                case RESTORE_FINISHED:
+                    restoreFinished();
+                    break;
+
                 case FINAL:
                     if (!mFinished) finalizeRestore();
                     else {
@@ -6529,7 +6545,7 @@
         private  void startRestore() {
             sendStartRestore(mAcceptSet.size());
 
-            UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
+            UnifiedRestoreState nextState = UnifiedRestoreState.RESTORE_FINISHED;
             try {
                 // If we don't yet have PM metadata for this token, synthesize an
                 // entry for the PM pseudopackage and make it the first to be
@@ -6544,6 +6560,7 @@
                     mPmAgent = new PackageManagerBackupAgent(metadataFile);
                 } catch (IOException e) {
                     // Nope, we need to get it via restore
+                    if (MORE_DEBUG) Slog.v(TAG, "Need to restore @pm@");
                     PackageInfo pmPackage = new PackageInfo();
                     pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mAcceptSet.add(0, pmPackage);
@@ -6581,8 +6598,11 @@
                     mCurrentPackage = new PackageInfo();
                     mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
-                    initiateOneRestore(mCurrentPackage, 0,
-                            IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
+                    mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
+                    if (MORE_DEBUG) {
+                        Slog.v(TAG, "initiating restore for PMBA");
+                    }
+                    initiateOneRestore(mCurrentPackage, 0);
                     // The PM agent called operationComplete() already, because our invocation
                     // of it is process-local and therefore synchronous.  That means that a
                     // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
@@ -6621,6 +6641,11 @@
                         nextState = UnifiedRestoreState.FINAL;
                         return;
                     }
+                } else {
+                    // We have the PMBA already, so we can proceed directly to
+                    // the RUNNING_QUEUE state ourselves.
+                    if (MORE_DEBUG) Slog.v(TAG, "PMBA from cache; proceeding to run queue");
+                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
                 }
             } catch (RemoteException e) {
                 // If we lost the transport at any time, halt
@@ -6762,10 +6787,10 @@
             }
 
             // Good to go!  Set up and bind the agent...
-            IBackupAgent agent = bindToAgentSynchronous(
+            mAgent = bindToAgentSynchronous(
                     mCurrentPackage.applicationInfo,
                     IApplicationThread.BACKUP_MODE_INCREMENTAL);
-            if (agent == null) {
+            if (mAgent == null) {
                 Slog.w(TAG, "Can't find backup agent for " + packageName);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
                         "Restore agent missing");
@@ -6775,7 +6800,7 @@
 
             // And then finally start the restore on this agent
             try {
-                initiateOneRestore(mCurrentPackage, metaInfo.versionCode, agent);
+                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
                 ++mCount;
             } catch (Exception e) {
                 Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -6785,7 +6810,7 @@
         }
 
         // Guts of a key/value restore operation
-        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
+        void initiateOneRestore(PackageInfo app, int appVersionCode) {
             final String packageName = app.packageName;
 
             if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
@@ -6881,7 +6906,7 @@
                 // the operationComplete() callback will schedule the next step,
                 // so we do not do that here.
                 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
-                agent.doRestore(mBackupData, appVersionCode, mNewState,
+                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                         token, mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
@@ -6926,6 +6951,20 @@
             }
         }
 
+        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
+        private void restoreFinished() {
+            try {
+                final int token = generateToken();
+                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
+                mAgent.doRestoreFinished(token, mBackupManagerBinder);
+                // If we get this far, the callback or timeout will schedule the
+                // next restore state, so we're done
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName);
+                executeNextState(UnifiedRestoreState.FINAL);
+            }
+        }
+
         class StreamFeederThread extends RestoreEngine implements Runnable {
             final String TAG = "StreamFeederThread";
             FullRestoreEngine mEngine;
@@ -7202,23 +7241,58 @@
 
         @Override
         public void operationComplete() {
-            int size = (int) mBackupDataName.length();
-            EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
-
-            // Just go back to running the restore queue
-            keyValueAgentCleanup();
-
-            // If there was widget state associated with this app, get the OS to
-            // incorporate it into current bookeeping and then pass that along to
-            // the app as part of the restore operation.
-            if (mWidgetData != null) {
-                restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "operationComplete() during restore: target="
+                        + mCurrentPackage.packageName
+                        + " state=" + mState);
             }
 
-            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
+            final UnifiedRestoreState nextState;
+            switch (mState) {
+                case RESTORE_KEYVALUE:
+                case RESTORE_FULL: {
+                    // Okay, we've just heard back from the agent that it's done with
+                    // the restore itself.  We now have to send the same agent its
+                    // doRestoreFinished() callback, so roll into that state.
+                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
+                    break;
+                }
+
+                case RESTORE_FINISHED: {
+                    // Okay, we're done with this package.  Tidy up and go on to the next
+                    // app in the queue.
+                    int size = (int) mBackupDataName.length();
+                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
+                            mCurrentPackage.packageName, size);
+
+                    // Just go back to running the restore queue
+                    keyValueAgentCleanup();
+
+                    // If there was widget state associated with this app, get the OS to
+                    // incorporate it into current bookeeping and then pass that along to
+                    // the app as part of the restore-time work.
+                    if (mWidgetData != null) {
+                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+                    }
+
+                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
+                    break;
+                }
+
+                default: {
+                    // Some kind of horrible semantic error; we're in an unexpected state.
+                    // Back off hard and wind up.
+                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
+                    keyValueAgentErrorCleanup();
+                    nextState = UnifiedRestoreState.FINAL;
+                    break;
+                }
+            }
+
+            executeNextState(nextState);
         }
 
-        // A call to agent.doRestore() has timed out
+        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
         @Override
         public void handleTimeout() {
             Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
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/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e152ebe..36d67ee 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -871,18 +871,85 @@
         checkManageAccountsPermission();
         UserHandle user = Binder.getCallingUserHandle();
         UserAccounts accounts = getUserAccountsForCaller();
-        if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) {
+        int userId = Binder.getCallingUserHandle().getIdentifier();
+        if (!canUserModifyAccounts(userId)) {
             try {
+                // TODO: This should be ERROR_CODE_USER_RESTRICTED instead. See http://b/16322768
                 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
                         "User cannot modify accounts");
             } catch (RemoteException re) {
             }
             return;
         }
+        if (!canUserModifyAccountsForType(userId, account.type)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
+            }
+            return;
+        }
 
         long identityToken = clearCallingIdentity();
 
         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
+        synchronized (accounts.credentialsPermissionNotificationIds) {
+            for (Pair<Pair<Account, String>, Integer> pair:
+                accounts.credentialsPermissionNotificationIds.keySet()) {
+                if (account.equals(pair.first.first)) {
+                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
+                    cancelNotification(id, user);
+                }
+            }
+        }
+
+        try {
+            new RemoveAccountSession(accounts, response, account).bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    @Override
+    public void removeAccountAsUser(IAccountManagerResponse response, Account account,
+            int userId) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "removeAccount: " + account
+                    + ", response " + response
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid()
+                    + ", for user id " + userId);
+        }
+        if (response == null) throw new IllegalArgumentException("response is null");
+        if (account == null) throw new IllegalArgumentException("account is null");
+
+        // Only allow the system process to modify accounts of other users
+        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+                    + " trying to remove account for " + userId);
+        checkManageAccountsPermission();
+
+        UserAccounts accounts = getUserAccounts(userId);
+        if (!canUserModifyAccounts(userId)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+                        "User cannot modify accounts");
+            } catch (RemoteException re) {
+            }
+            return;
+        }
+        if (!canUserModifyAccountsForType(userId, account.type)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
+            }
+            return;
+        }
+
+        UserHandle user = new UserHandle(userId);
+        long identityToken = clearCallingIdentity();
+
+        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
         synchronized(accounts.credentialsPermissionNotificationIds) {
             for (Pair<Pair<Account, String>, Integer> pair:
                 accounts.credentialsPermissionNotificationIds.keySet()) {
@@ -1526,20 +1593,23 @@
         checkManageAccountsPermission();
 
         // Is user disallowed from modifying accounts?
-        if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) {
+        int userId = Binder.getCallingUserHandle().getIdentifier();
+        if (!canUserModifyAccounts(userId)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                         "User is not allowed to add an account!");
             } catch (RemoteException re) {
             }
-            Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
-            cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            long identityToken = clearCallingIdentity();
+            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED);
+            return;
+        }
+        if (!canUserModifyAccountsForType(userId, accountType)) {
             try {
-                mContext.startActivityAsUser(cantAddAccount, UserHandle.CURRENT);
-            } finally {
-                restoreCallingIdentity(identityToken);
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
             }
+            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
             return;
         }
 
@@ -1576,6 +1646,92 @@
     }
 
     @Override
+    public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "addAccount: accountType " + accountType
+                    + ", response " + response
+                    + ", authTokenType " + authTokenType
+                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid()
+                    + ", for user id " + userId);
+        }
+        if (response == null) throw new IllegalArgumentException("response is null");
+        if (accountType == null) throw new IllegalArgumentException("accountType is null");
+        checkManageAccountsPermission();
+
+        // Only allow the system process to add accounts of other users
+        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+                    + " trying to add account for " + userId);
+
+        // Is user disallowed from modifying accounts?
+        if (!canUserModifyAccounts(userId)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+                        "User is not allowed to add an account!");
+            } catch (RemoteException re) {
+            }
+            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED);
+            return;
+        }
+        if (!canUserModifyAccountsForType(userId, accountType)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
+            }
+            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
+            return;
+        }
+
+        UserAccounts accounts = getUserAccounts(userId);
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
+        options.putInt(AccountManager.KEY_CALLER_UID, uid);
+        options.putInt(AccountManager.KEY_CALLER_PID, pid);
+
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(accounts, response, accountType, expectActivityLaunch,
+                    true /* stripAuthTokenFromResult */) {
+                @Override
+                public void run() throws RemoteException {
+                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
+                            options);
+                }
+
+                @Override
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", addAccount"
+                            + ", accountType " + accountType
+                            + ", requiredFeatures "
+                            + (requiredFeatures != null
+                              ? TextUtils.join(",", requiredFeatures)
+                              : null);
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void showCantAddAccount(int errorCode) {
+        Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
+        cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
+        cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        long identityToken = clearCallingIdentity();
+        try {
+            mContext.startActivity(cantAddAccount);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    @Override
     public void confirmCredentialsAsUser(IAccountManagerResponse response,
             final Account account, final Bundle options, final boolean expectActivityLaunch,
             int userId) {
@@ -2766,18 +2922,18 @@
                 Manifest.permission.USE_CREDENTIALS);
     }
 
-    private boolean canUserModifyAccounts(int callingUid, String accountType) {
-        if (callingUid != Process.myUid()) {
-            if (getUserManager().getUserRestrictions(
-                    new UserHandle(UserHandle.getUserId(callingUid)))
-                    .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
-                return false;
-            }
+    private boolean canUserModifyAccounts(int userId) {
+        if (getUserManager().getUserRestrictions(new UserHandle(userId))
+                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
+            return false;
         }
+        return true;
+    }
 
+    private boolean canUserModifyAccountsForType(int userId, String accountType) {
         DevicePolicyManager dpm = (DevicePolicyManager) mContext
                 .getSystemService(Context.DEVICE_POLICY_SERVICE);
-        String[] typesArray = dpm.getAccountTypesWithManagementDisabled();
+        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
         for (String forbiddenType : typesArray) {
             if (forbiddenType.equals(accountType)) {
                 return false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1316da1..31c1c6c 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -364,28 +364,6 @@
     // devices.
     private boolean mShowDialogs = true;
 
-    /**
-     * Description of a request to start a new activity, which has been held
-     * due to app switches being disabled.
-     */
-    static class PendingActivityLaunch {
-        final ActivityRecord r;
-        final ActivityRecord sourceRecord;
-        final int startFlags;
-        final ActivityStack stack;
-
-        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
-                int _startFlags, ActivityStack _stack) {
-            r = _r;
-            sourceRecord = _sourceRecord;
-            startFlags = _startFlags;
-            stack = _stack;
-        }
-    }
-
-    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
-            = new ArrayList<PendingActivityLaunch>();
-
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
@@ -1438,7 +1416,7 @@
             } break;
             case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    doPendingActivityLaunchesLocked(true);
+                    mStackSupervisor.doPendingActivityLaunchesLocked(true);
                 }
             } break;
             case KILL_APPLICATION_MSG: {
@@ -3339,19 +3317,6 @@
         mProcessObservers.finishBroadcast();
     }
 
-    final void doPendingActivityLaunchesLocked(boolean doResume) {
-        final int N = mPendingActivityLaunches.size();
-        if (N <= 0) {
-            return;
-        }
-        for (int i=0; i<N; i++) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
-            mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                    doResume && i == (N-1), null);
-        }
-        mPendingActivityLaunches.clear();
-    }
-
     @Override
     public final int startActivity(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo,
@@ -4715,6 +4680,28 @@
         }
     }
 
+    @Override
+    public void addPackageDependency(String packageName) {
+        synchronized (this) {
+            int callingPid = Binder.getCallingPid();
+            if (callingPid == Process.myPid()) {
+                //  Yeah, um, no.
+                Slog.w(TAG, "Can't addPackageDependency on system process");
+                return;
+            }
+            ProcessRecord proc;
+            synchronized (mPidsSelfLocked) {
+                proc = mPidsSelfLocked.get(Binder.getCallingPid());
+            }
+            if (proc != null) {
+                if (proc.pkgDeps == null) {
+                    proc.pkgDeps = new ArraySet<String>(1);
+                }
+                proc.pkgDeps.add(packageName);
+            }
+        }
+    }
+
     /*
      * The pkg name and app id have to be specified.
      */
@@ -4911,7 +4898,6 @@
         // Remove all processes this package may have touched: all with the
         // same UID (except for the system or root user), and all whose name
         // matches the package name.
-        final String procNamePrefix = packageName != null ? (packageName + ":") : null;
         final int NP = mProcessNames.getMap().size();
         for (int ip=0; ip<NP; ip++) {
             SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
@@ -4947,13 +4933,15 @@
                 // that match it.  We need to qualify this by the processes
                 // that are running under the specified app and user ID.
                 } else {
-                    if (UserHandle.getAppId(app.uid) != appId) {
+                    final boolean isDep = app.pkgDeps != null
+                            && app.pkgDeps.contains(packageName);
+                    if (!isDep && UserHandle.getAppId(app.uid) != appId) {
                         continue;
                     }
                     if (userId != UserHandle.USER_ALL && app.userId != userId) {
                         continue;
                     }
-                    if (!app.pkgList.containsKey(packageName)) {
+                    if (!app.pkgList.containsKey(packageName) && !isDep) {
                         continue;
                     }
                 }
@@ -8054,7 +8042,7 @@
                 checkedGrants = true;
             }
             userId = handleIncomingUser(callingPid, callingUid, userId,
-                    false, ALLOW_NON_FULL_IN_PROFILE,
+                    false, ALLOW_NON_FULL,
                     "checkContentProviderPermissionLocked " + cpi.authority, null);
             if (userId != tmpTargetUserId) {
                 // When we actually went to determine the final targer user ID, this ended
@@ -9117,6 +9105,7 @@
         }
     }
 
+    @Override
     public void stopAppSwitches() {
         if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -11030,30 +11019,15 @@
         return errList;
     }
 
-    static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
-        if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
-            if (currApp != null) {
-                currApp.lru = adj - ProcessList.CACHED_APP_MIN_ADJ + 1;
-            }
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
-        } else if (adj >= ProcessList.SERVICE_B_ADJ) {
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
-        } else if (adj >= ProcessList.HOME_APP_ADJ) {
-            if (currApp != null) {
-                currApp.lru = 0;
-            }
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
-        } else if (adj >= ProcessList.SERVICE_ADJ) {
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
-        } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
-        } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
-        } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+    static int procStateToImportance(int procState, int memAdj,
+            ActivityManager.RunningAppProcessInfo currApp) {
+        int imp = ActivityManager.RunningAppProcessInfo.procStateToImportance(procState);
+        if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+            currApp.lru = memAdj;
         } else {
-            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+            currApp.lru = 0;
         }
+        return imp;
     }
 
     private void fillInProcMemInfo(ProcessRecord app,
@@ -11071,7 +11045,8 @@
         }
         outInfo.lastTrimLevel = app.trimMemoryLevel;
         int adj = app.curAdj;
-        outInfo.importance = oomAdjToImportance(adj, outInfo);
+        int procState = app.curProcState;
+        outInfo.importance = procStateToImportance(procState, adj, outInfo);
         outInfo.importanceReasonCode = app.adjTypeCode;
         outInfo.processState = app.curProcState;
     }
@@ -11098,8 +11073,9 @@
                     fillInProcMemInfo(app, currApp);
                     if (app.adjSource instanceof ProcessRecord) {
                         currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
-                        currApp.importanceReasonImportance = oomAdjToImportance(
-                                app.adjSourceOom, null);
+                        currApp.importanceReasonImportance =
+                                ActivityManager.RunningAppProcessInfo.procStateToImportance(
+                                        app.adjSourceProcState);
                     } else if (app.adjSource instanceof ActivityRecord) {
                         ActivityRecord r = (ActivityRecord)app.adjSource;
                         if (r.app != null) currApp.importanceReasonPid = r.app.pid;
@@ -15615,7 +15591,7 @@
                             app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                     .REASON_SERVICE_IN_USE;
                             app.adjSource = cr.binding.client;
-                            app.adjSourceOom = clientAdj;
+                            app.adjSourceProcState = clientProcState;
                             app.adjTarget = s.name;
                         }
                     }
@@ -15636,7 +15612,7 @@
                             app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                     .REASON_SERVICE_IN_USE;
                             app.adjSource = a;
-                            app.adjSourceOom = adj;
+                            app.adjSourceProcState = procState;
                             app.adjTarget = s.name;
                         }
                     }
@@ -15681,7 +15657,7 @@
                     app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                             .REASON_PROVIDER_IN_USE;
                     app.adjSource = client;
-                    app.adjSourceOom = clientAdj;
+                    app.adjSourceProcState = clientProcState;
                     app.adjTarget = cpr.name;
                 }
                 if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 32f2624..91bc7e3 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2732,18 +2732,23 @@
         return r;
     }
 
-    void finishAllActivitiesLocked() {
+    void finishAllActivitiesLocked(boolean immediately) {
+        boolean noActivitiesInStack = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
-                if (r.finishing) {
+                noActivitiesInStack = false;
+                if (r.finishing && !immediately) {
                     continue;
                 }
-                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
                 finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
             }
         }
+        if (noActivitiesInStack) {
+            mActivityContainer.onTaskListEmptyLocked();
+        }
     }
 
     final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
@@ -2858,6 +2863,7 @@
         // down to the max limit while they are still waiting to finish.
         mStackSupervisor.mFinishingActivities.remove(r);
         mStackSupervisor.mWaitingVisibleActivities.remove(r);
+        mStackSupervisor.removePendingActivityLaunchesLocked(r);
 
         // Remove any pending results.
         if (r.finishing && r.pendingResults != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b0dfe4a..9d1f8c6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -98,7 +98,6 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.wm.WindowManagerService;
 
@@ -271,6 +270,28 @@
      */
     private LockTaskNotify mLockTaskNotify;
 
+    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
+            = new ArrayList<PendingActivityLaunch>();
+
+    /**
+     * Description of a request to start a new activity, which has been held
+     * due to app switches being disabled.
+     */
+    static class PendingActivityLaunch {
+        final ActivityRecord r;
+        final ActivityRecord sourceRecord;
+        final int startFlags;
+        final ActivityStack stack;
+
+        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
+                int _startFlags, ActivityStack _stack) {
+            r = _r;
+            sourceRecord = _sourceRecord;
+            startFlags = _startFlags;
+            stack = _stack;
+        }
+    }
+
     public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
         mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
@@ -886,6 +907,8 @@
                     requestCode, callingPid, callingUid, callingPackage, startFlags, options,
                     componentSpecified, null, container);
 
+            Binder.restoreCallingIdentity(origId);
+
             if (stack.mConfigWillChange) {
                 // If the caller also wants to switch to a new configuration,
                 // do so now.  This allows a clean switch, as we are waiting
@@ -899,8 +922,6 @@
                 mService.updateConfigurationLocked(config, null, false, false);
             }
 
-            Binder.restoreCallingIdentity(origId);
-
             if (outResult != null) {
                 outResult.result = res;
                 if (res == ActivityManager.START_SUCCESS) {
@@ -1421,7 +1442,7 @@
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
                 PendingActivityLaunch pal =
                         new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
-                mService.mPendingActivityLaunches.add(pal);
+                mPendingActivityLaunches.add(pal);
                 setDismissKeyguard(false);
                 ActivityOptions.abort(options);
                 return ActivityManager.START_SWITCHES_CANCELED;
@@ -1439,7 +1460,7 @@
             mService.mDidAppSwitch = true;
         }
 
-        mService.doPendingActivityLaunchesLocked(false);
+        doPendingActivityLaunchesLocked(false);
 
         err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                 startFlags, true, options);
@@ -2008,6 +2029,23 @@
         return ActivityManager.START_SUCCESS;
     }
 
+    final void doPendingActivityLaunchesLocked(boolean doResume) {
+        while (!mPendingActivityLaunches.isEmpty()) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+            startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                    doResume && mPendingActivityLaunches.isEmpty(), null);
+        }
+    }
+
+    void removePendingActivityLaunchesLocked(ActivityRecord r) {
+        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            if (pal.r == r) {
+                mPendingActivityLaunches.remove(palNdx);
+            }
+        }
+    }
+
     void acquireLaunchWakelock() {
         if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
             throw new IllegalStateException("Calling must be system uid");
@@ -3312,7 +3350,9 @@
                     synchronized (mService) {
                         Slog.w(TAG, "Timeout waiting for all activities in task to finish. " +
                                 msg.obj);
-                        ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
+                        final ActivityContainer container = (ActivityContainer) msg.obj;
+                        container.mStack.finishAllActivitiesLocked(true);
+                        container.onTaskListEmptyLocked();
                     }
                 } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
@@ -3414,11 +3454,11 @@
 
                 final Message msg =
                         mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
-                mHandler.sendMessageDelayed(msg, 1000);
+                mHandler.sendMessageDelayed(msg, 2000);
 
                 long origId = Binder.clearCallingIdentity();
                 try {
-                    mStack.finishAllActivitiesLocked();
+                    mStack.finishAllActivitiesLocked(false);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
@@ -3717,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/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a20be73..b33f7b7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -55,6 +55,7 @@
     // List of packages running in the process
     final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
             = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
+    ArraySet<String> pkgDeps;   // additional packages we have a dependency on
     IApplicationThread thread;  // the actual proc...  may be null only if
                                 // 'persistent' is true (in which case we
                                 // are in the process of launching the app)
@@ -124,7 +125,7 @@
     String adjType;             // Debugging: primary thing impacting oom_adj.
     int adjTypeCode;            // Debugging: adj code to report to app.
     Object adjSource;           // Debugging: option dependent object.
-    int adjSourceOom;           // Debugging: oom_adj of adjSource's process.
+    int adjSourceProcState;     // Debugging: proc state of adjSource's process.
     Object adjTarget;           // Debugging: target component impacting oom_adj.
     
     // contains HistoryRecord objects
@@ -195,6 +196,14 @@
             pw.print(pkgList.keyAt(i));
         }
         pw.println("}");
+        if (pkgDeps != null) {
+            pw.print(prefix); pw.print("packageDependencies={");
+            for (int i=0; i<pkgDeps.size(); i++) {
+                if (i > 0) pw.print(", ");
+                pw.print(pkgDeps.valueAt(i));
+            }
+            pw.println("}");
+        }
         pw.print(prefix); pw.print("compat="); pw.println(compat);
         if (instrumentationClass != null || instrumentationProfileFile != null
                 || instrumentationArguments != null) {
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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f0ec3bb..0da1fa4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -815,6 +815,7 @@
                 final CharSequence body = res.getText(R.string.data_usage_limit_body);
 
                 final CharSequence title;
+                int icon = R.drawable.stat_notify_disabled_data;
                 switch (policy.template.getMatchRule()) {
                     case MATCH_MOBILE_3G_LOWER:
                         title = res.getText(R.string.data_usage_3g_limit_title);
@@ -827,6 +828,7 @@
                         break;
                     case MATCH_WIFI:
                         title = res.getText(R.string.data_usage_wifi_limit_title);
+                        icon = R.drawable.stat_notify_error;
                         break;
                     default:
                         title = null;
@@ -834,7 +836,7 @@
                 }
 
                 builder.setOngoing(true);
-                builder.setSmallIcon(R.drawable.stat_notify_disabled);
+                builder.setSmallIcon(icon);
                 builder.setTicker(title);
                 builder.setContentTitle(title);
                 builder.setContentText(body);
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index f4248daf..a06daf6 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -273,7 +273,7 @@
         }
     }
 
-    public void setZenModeCondition(Uri conditionId) {
+    public void setZenModeCondition(Uri conditionId, String reason) {
         if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
         synchronized(mMutex) {
             ComponentName conditionComponent = null;
@@ -306,6 +306,7 @@
             if (!Objects.equals(mExitConditionId, conditionId)) {
                 mExitConditionId = conditionId;
                 mExitConditionComponent = conditionComponent;
+                ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, reason);
                 saveZenConfigLocked();
             }
         }
@@ -314,15 +315,16 @@
     private void subscribeLocked(ConditionRecord r) {
         if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
         final IConditionProvider provider = provider(r);
-        if (provider == null) {
-            Slog.w(TAG, "subscribeLocked: no provider");
-            return;
+        RemoteException re = null;
+        if (provider != null) {
+            try {
+                provider.onSubscribe(r.id);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error subscribing to " + r, e);
+                re = e;
+            }
         }
-        try {
-            provider.onSubscribe(r.id);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Error subscribing to " + r, e);
-        }
+        ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
     }
 
     private static <T> ArraySet<T> safeSet(T... items) {
@@ -388,15 +390,16 @@
     private void unsubscribeLocked(ConditionRecord r) {
         if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
         final IConditionProvider provider = provider(r);
-        if (provider == null) {
-            Slog.w(TAG, "unsubscribeLocked: no provider");
-            return;
+        RemoteException re = null;
+        if (provider != null) {
+            try {
+                provider.onUnsubscribe(r.id);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error unsubscribing to " + r, e);
+                re = e;
+            }
         }
-        try {
-            provider.onUnsubscribe(r.id);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Error unsubscribing to " + r, e);
-        }
+        ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
     }
 
     private static IConditionProvider provider(ConditionRecord r) {
@@ -433,8 +436,12 @@
             return;
         }
         synchronized (mMutex) {
+            final boolean changingExit = !Objects.equals(mExitConditionId, config.exitConditionId);
             mExitConditionId = config.exitConditionId;
             mExitConditionComponent = config.exitConditionComponent;
+            if (changingExit) {
+                ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, "config");
+            }
             if (config.conditionComponents == null || config.conditionIds == null
                     || config.conditionComponents.length != config.conditionIds.length) {
                 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -498,7 +505,7 @@
             final int mode = mZenModeHelper.getZenMode();
             if (mode == Global.ZEN_MODE_OFF) {
                 // ensure any manual condition is cleared
-                setZenModeCondition(null);
+                setZenModeCondition(null, "zenOff");
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b15ac8d..aa9e151 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1282,7 +1282,7 @@
             enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
             final long identity = Binder.clearCallingIdentity();
             try {
-                mConditionProviders.setZenModeCondition(conditionId);
+                mConditionProviders.setZenModeCondition(conditionId, "binderCall");
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1409,6 +1409,9 @@
             if (filter == null || zenOnly) {
                 pw.println("\n  Zen Mode:");
                 mZenModeHelper.dump(pw, "    ");
+
+                pw.println("\n  Zen Log:");
+                ZenLog.dump(pw, "    ");
             }
 
             if (!zenOnly) {
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
new file mode 100644
index 0000000..81f64b1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -0,0 +1,164 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.content.ComponentName;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
+import android.provider.Settings.Global;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ZenModeConfig;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+public class ZenLog {
+    private static final String TAG = "ZenLog";
+
+    private static final int SIZE = Build.IS_DEBUGGABLE ? 100 : 20;
+
+    private static final long[] TIMES = new long[SIZE];
+    private static final int[] TYPES = new int[SIZE];
+    private static final String[] MSGS = new String[SIZE];
+
+    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
+    private static final int TYPE_INTERCEPTED = 1;
+    private static final int TYPE_ALLOW_DISABLE = 2;
+    private static final int TYPE_SET_RINGER_MODE = 3;
+    private static final int TYPE_DOWNTIME = 4;
+    private static final int TYPE_ZEN_MODE = 5;
+    private static final int TYPE_EXIT_CONDITION = 6;
+    private static final int TYPE_SUBSCRIBE = 7;
+    private static final int TYPE_UNSUBSCRIBE = 8;
+    private static final int TYPE_CONFIG = 9;
+
+    private static int sNext;
+    private static int sSize;
+
+    public static void traceIntercepted(NotificationRecord record, String reason) {
+        if (record != null && record.isIntercepted()) return;  // already logged
+        append(TYPE_INTERCEPTED, record.getKey() + "," + reason);
+    }
+
+    public static void traceAllowDisable(String pkg, boolean allowDisable, String reason) {
+        append(TYPE_ALLOW_DISABLE, allowDisable + "," + pkg + "," + reason);
+    }
+
+    public static void traceSetRingerMode(int ringerMode) {
+        append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode));
+    }
+
+    public static void traceDowntime(boolean enter, int day, int[] days) {
+        append(TYPE_DOWNTIME, enter + ",day=" + day + ",days=" + (days != null ? Arrays.asList(days)
+                : null));
+    }
+
+    public static void traceUpdateZenMode(int fromMode, int toMode) {
+        append(TYPE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
+    }
+
+    public static void traceExitCondition(Uri id, ComponentName component, String reason) {
+        append(TYPE_EXIT_CONDITION, id + "," + componentToString(component) + "," + reason);
+    }
+
+    public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
+        append(TYPE_SUBSCRIBE, uri + "," + subscribeResult(provider, e));
+    }
+
+    public static void traceUnsubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
+        append(TYPE_UNSUBSCRIBE, uri + "," + subscribeResult(provider, e));
+    }
+
+    public static void traceConfig(ZenModeConfig oldConfig, ZenModeConfig newConfig) {
+        append(TYPE_CONFIG, newConfig != null ? newConfig.toString() : null);
+    }
+
+    private static String subscribeResult(IConditionProvider provider, RemoteException e) {
+        return provider == null ? "no provider" : e != null ? e.getMessage() : "ok";
+    }
+
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_INTERCEPTED: return "intercepted";
+            case TYPE_ALLOW_DISABLE: return "allow_disable";
+            case TYPE_SET_RINGER_MODE: return "set_ringer_mode";
+            case TYPE_DOWNTIME: return "downtime";
+            case TYPE_ZEN_MODE: return "zen_mode";
+            case TYPE_EXIT_CONDITION: return "exit_condition";
+            case TYPE_SUBSCRIBE: return "subscribe";
+            case TYPE_UNSUBSCRIBE: return "unsubscribe";
+            case TYPE_CONFIG: return "config";
+            default: return "unknown";
+        }
+    }
+
+    private static String ringerModeToString(int ringerMode) {
+        switch (ringerMode) {
+            case AudioManager.RINGER_MODE_SILENT: return "silent";
+            case AudioManager.RINGER_MODE_VIBRATE: return "vibrate";
+            case AudioManager.RINGER_MODE_NORMAL: return "normal";
+            default: return "unknown";
+        }
+    }
+
+    private static String zenModeToString(int zenMode) {
+        switch (zenMode) {
+            case Global.ZEN_MODE_OFF: return "off";
+            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return "important_interruptions";
+            case Global.ZEN_MODE_NO_INTERRUPTIONS: return "no_interruptions";
+            default: return "unknown";
+        }
+    }
+
+    private static String componentToString(ComponentName component) {
+        return component != null ? component.toShortString() : null;
+    }
+
+    private static void append(int type, String msg) {
+        synchronized(MSGS) {
+            TIMES[sNext] = System.currentTimeMillis();
+            TYPES[sNext] = type;
+            MSGS[sNext] = msg;
+            sNext = (sNext + 1) % SIZE;
+            if (sSize < SIZE) {
+                sSize++;
+            }
+        }
+        Slog.d(TAG, typeToString(type) + ": " + msg);
+    }
+
+    public static void dump(PrintWriter pw, String prefix) {
+        synchronized(MSGS) {
+            final int start = (sNext - sSize + SIZE) % SIZE;
+            for (int i = 0; i < sSize; i++) {
+                final int j = (start + i) % SIZE;
+                pw.print(prefix);
+                pw.print(FORMAT.format(new Date(TIMES[j])));
+                pw.print(' ');
+                pw.print(typeToString(TYPES[j]));
+                pw.print(": ");
+                pw.println(MSGS[j]);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f74e371..118b9fc 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -139,18 +139,32 @@
                 return false;
             }
             if (isAlarm(record)) {
-                return mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+                if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+                    ZenLog.traceIntercepted(record, "alarm");
+                    return true;
+                }
+                return false;
             }
             // audience has veto power over all following rules
             if (!audienceMatches(record)) {
+                ZenLog.traceIntercepted(record, "!audienceMatches");
                 return true;
             }
             if (isCall(record)) {
-                return !mConfig.allowCalls;
+                if (!mConfig.allowCalls) {
+                    ZenLog.traceIntercepted(record, "!allowCalls");
+                    return true;
+                }
+                return false;
             }
             if (isMessage(record)) {
-                return !mConfig.allowMessages;
+                if (!mConfig.allowMessages) {
+                    ZenLog.traceIntercepted(record, "!allowMessages");
+                    return true;
+                }
+                return false;
             }
+            ZenLog.traceIntercepted(record, "!allowed");
             return true;
         }
         return false;
@@ -171,6 +185,7 @@
             Slog.d(TAG, String.format("updateZenMode: %s -> %s",
                     Global.zenModeToString(mZenMode),
                     Global.zenModeToString(mode)));
+            ZenLog.traceUpdateZenMode(mZenMode, mode);
         }
         mZenMode = mode;
         final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
@@ -202,30 +217,41 @@
         // force ringer mode into compliance
         if (mAudioManager != null) {
             int ringerMode = mAudioManager.getRingerMode();
+            int forcedRingerMode = -1;
             if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
                 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
                     mPreviousRingerMode = ringerMode;
                     Slog.d(TAG, "Silencing ringer");
-                    mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+                    forcedRingerMode = AudioManager.RINGER_MODE_SILENT;
                 }
             } else {
                 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
                     Slog.d(TAG, "Unsilencing ringer");
-                    mAudioManager.setRingerMode(mPreviousRingerMode != -1 ? mPreviousRingerMode
-                            : AudioManager.RINGER_MODE_NORMAL);
+                    forcedRingerMode = mPreviousRingerMode != -1 ? mPreviousRingerMode
+                            : AudioManager.RINGER_MODE_NORMAL;
                     mPreviousRingerMode = -1;
                 }
             }
+            if (forcedRingerMode != -1) {
+                mAudioManager.setRingerMode(forcedRingerMode);
+                ZenLog.traceSetRingerMode(forcedRingerMode);
+            }
         }
         dispatchOnZenModeChanged();
     }
 
     public boolean allowDisable(int what, IBinder token, String pkg) {
         // TODO(cwren): delete this API before the next release. Bug:15344099
+        boolean allowDisable = true;
+        String reason = null;
         if (isDefaultPhoneApp(pkg)) {
-            return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
+            allowDisable = mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
+            reason = mZenMode == Global.ZEN_MODE_OFF ? "zenOff" : "allowCalls";
         }
-        return true;
+        if (!SYSTEM_PACKAGES.contains(pkg)) {
+            ZenLog.traceAllowDisable(pkg, allowDisable, reason);
+        }
+        return allowDisable;
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -255,8 +281,8 @@
     public boolean setConfig(ZenModeConfig config) {
         if (config == null || !config.isValid()) return false;
         if (config.equals(mConfig)) return true;
+        ZenLog.traceConfig(mConfig, config);
         mConfig = config;
-        Slog.d(TAG, "mConfig=" + mConfig);
         dispatchOnConfigChanged();
         final String val = Integer.toString(mConfig.hashCode());
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
@@ -398,20 +424,18 @@
                     intent.getAction(), ts(schTime), ts(now), now - schTime));
 
             final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
+            boolean enter = false;
+            final int day = getDayOfWeek(schTime);
             if (days != null) {
-                final int day = getDayOfWeek(schTime);
                 for (int i = 0; i < days.length; i++) {
                     if (days[i] == day) {
-                        Slog.d(TAG, "Enter downtime, day=" + day + " days=" + Arrays.asList(days));
+                        enter = true;
                         ZenModeHelper.this.setZenMode(zenModeValue);
                         break;
-                    } else {
-                        Slog.d(TAG, "Skip downtime, day=" + day + " days=" + Arrays.asList(days));
                     }
                 }
-            } else {
-                Slog.d(TAG, "Skip downtime, no days configured");
             }
+            ZenLog.traceDowntime(enter, day, days);
             updateAlarms();
         }
 
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 65cb6c9..09cf392 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -269,13 +269,39 @@
 
             Intent launchIntent = new Intent(Intent.ACTION_MAIN);
             launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-            launchIntent.setComponent(component);
             launchIntent.setSourceBounds(sourceBounds);
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            launchIntent.setPackage(component.getPackageName());
 
             long ident = Binder.clearCallingIdentity();
             try {
-                mContext.startActivityAsUser(launchIntent, opts, user);
+                IPackageManager pm = AppGlobals.getPackageManager();
+                ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+                if (!info.exported) {
+                    throw new SecurityException("Cannot launch non-exported components "
+                            + component);
+                }
+
+                // Check that the component actually has Intent.CATEGORY_LAUCNCHER
+                // as calling startActivityAsUser ignores the category and just
+                // resolves based on the component if present.
+                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
+                        PackageManager.NO_CROSS_PROFILE, // We only want the apps for this user
+                        user.getIdentifier());
+                final int size = apps.size();
+                for (int i = 0; i < size; ++i) {
+                    ActivityInfo activityInfo = apps.get(i).activityInfo;
+                    if (activityInfo.packageName.equals(component.getPackageName()) &&
+                            activityInfo.name.equals(component.getClassName())) {
+                        // Found an activity with category launcher that matches
+                        // this component so ok to launch.
+                        launchIntent.setComponent(component);
+                        mContext.startActivityAsUser(launchIntent, opts, user);
+                        return;
+                    }
+                }
+                throw new SecurityException("Attempt to launch activity without "
+                        + " category Intent.CATEGORY_LAUNCHER " + component);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 727cff0..a70dd1b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6231,31 +6231,26 @@
         info.nativeLibraryDir = null;
         info.secondaryNativeLibraryDir = null;
 
-        if (bundledApp) {
-            // Monolithic bundled install
-            // TODO: support cluster bundled installs?
-
-            final boolean is64Bit = (info.primaryCpuAbi != null)
-                    && VMRuntime.is64BitAbi(info.primaryCpuAbi);
-
-            // This is a bundled system app so choose the path based on the ABI.
-            // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
-            // is just the default path.
-            final String apkName = deriveCodePathName(codePath);
-            final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
-            info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
-                    apkName).getAbsolutePath();
-            info.nativeLibraryRootRequiresIsa = false;
-
-            info.nativeLibraryDir = info.nativeLibraryRootDir;
-            if (info.secondaryCpuAbi != null) {
-                final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
-                info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
-                        secondaryLibDir, apkName).getAbsolutePath();
-            }
-        } else if (isApkFile(codeFile)) {
+        if (isApkFile(codeFile)) {
             // Monolithic install
-            if (asecApp) {
+            if (bundledApp) {
+                final boolean is64Bit = VMRuntime.is64BitInstructionSet(
+                        getPrimaryInstructionSet(info));
+
+                // This is a bundled system app so choose the path based on the ABI.
+                // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
+                // is just the default path.
+                final String apkName = deriveCodePathName(codePath);
+                final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
+                info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
+                        apkName).getAbsolutePath();
+
+                if (info.secondaryCpuAbi != null) {
+                    final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
+                    info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
+                            secondaryLibDir, apkName).getAbsolutePath();
+                }
+            } else if (asecApp) {
                 info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
                         .getAbsolutePath();
             } else {
@@ -6271,10 +6266,8 @@
             info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
             info.nativeLibraryRootRequiresIsa = true;
 
-            if (info.primaryCpuAbi != null) {
-                info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
-                        VMRuntime.getInstructionSet(info.primaryCpuAbi)).getAbsolutePath();
-            }
+            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
+                    getPrimaryInstructionSet(info)).getAbsolutePath();
 
             if (info.secondaryCpuAbi != null) {
                 info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a3fd1df..e16894e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3171,25 +3171,34 @@
         file.delete();
         file = getUserPackagesStateBackupFile(userId);
         file.delete();
-        removeCrossProfileIntentFiltersToUserLPr(userId);
+        removeCrossProfileIntentFiltersLPw(userId);
         removeCrossProfilePackagesLPw(userId);
     }
 
-    void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
-        for (int i = 0; i < mCrossProfileIntentResolvers.size(); i++) {
-            int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
-            CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
-            boolean needsWriting = false;
-            HashSet<CrossProfileIntentFilter> cpifs =
-                    new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
-            for (CrossProfileIntentFilter cpif : cpifs) {
-                if (cpif.getTargetUserId() == targetUserId) {
-                    needsWriting = true;
-                    cpir.removeFilter(cpif);
-                }
+    void removeCrossProfileIntentFiltersLPw(int userId) {
+        synchronized (mCrossProfileIntentResolvers) {
+            // userId is the source user
+            if (mCrossProfileIntentResolvers.get(userId) != null) {
+                mCrossProfileIntentResolvers.remove(userId);
+                writePackageRestrictionsLPr(userId);
             }
-            if (needsWriting) {
-                writePackageRestrictionsLPr(sourceUserId);
+            // userId is the target user
+            int count = mCrossProfileIntentResolvers.size();
+            for (int i = 0; i < count; i++) {
+                int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
+                CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
+                boolean needsWriting = false;
+                HashSet<CrossProfileIntentFilter> cpifs =
+                        new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+                for (CrossProfileIntentFilter cpif : cpifs) {
+                    if (cpif.getTargetUserId() == userId) {
+                        needsWriting = true;
+                        cpir.removeFilter(cpif);
+                    }
+                }
+                if (needsWriting) {
+                    writePackageRestrictionsLPr(sourceUserId);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3882769..b4cc252 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -52,7 +52,6 @@
 import android.util.Xml;
 
 import com.android.internal.app.IAppOpsService;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
@@ -241,7 +240,6 @@
     }
 
     void systemReady() {
-        mUserPackageMonitor.register(mContext, null, UserHandle.ALL, false);
         userForeground(UserHandle.USER_OWNER);
         mAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -845,7 +843,8 @@
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
+        writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
+        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
         serializer.endTag(null, TAG_RESTRICTIONS);
     }
@@ -988,7 +987,8 @@
                 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
         readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
+        readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
+        readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
         readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
     }
 
@@ -1039,11 +1039,10 @@
     }
 
     /**
-     * Removes all the restrictions files (res_<packagename>) for a given user, if all is true,
-     * else removes only those packages that have been uninstalled.
+     * Removes all the restrictions files (res_<packagename>) for a given user.
      * Does not do any permissions checking.
      */
-    private void cleanAppRestrictions(int userId, boolean all) {
+    private void cleanAppRestrictions(int userId) {
         synchronized (mPackagesLock) {
             File dir = Environment.getUserSystemDirectory(userId);
             String[] files = dir.list();
@@ -1052,14 +1051,7 @@
                 if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
                     File resFile = new File(dir, fileName);
                     if (resFile.exists()) {
-                        if (all) {
-                            resFile.delete();
-                        } else {
-                            String pkg = restrictionsFileNameToPackage(fileName);
-                            if (!isPackageInstalled(pkg, userId)) {
-                                resFile.delete();
-                            }
-                        }
+                        resFile.delete();
                     }
                 }
             }
@@ -1326,15 +1318,21 @@
             checkManageUsersPermission("Only system can set restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
-            // Write the restrictions to XML
-            writeApplicationRestrictionsLocked(packageName, restrictions, userId);
+            if (restrictions == null || restrictions.isEmpty()) {
+                cleanAppRestrictionsForPackage(packageName, userId);
+            } else {
+                // Write the restrictions to XML
+                writeApplicationRestrictionsLocked(packageName, restrictions, userId);
+            }
         }
 
-        // Notify package of changes via an intent - only sent to explicitly registered receivers.
-        Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-        changeIntent.setPackage(packageName);
-        changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mContext.sendBroadcastAsUser(changeIntent, new UserHandle(userId));
+        if (isPackageInstalled(packageName, userId)) {
+            // Notify package of changes via an intent - only sent to explicitly registered receivers.
+            Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+            changeIntent.setPackage(packageName);
+            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            mContext.sendBroadcastAsUser(changeIntent, new UserHandle(userId));
+        }
     }
 
     @Override
@@ -1437,7 +1435,7 @@
             // Remove restrictions pin
             setRestrictionsChallenge(null);
             // Remove any app restrictions
-            cleanAppRestrictions(userHandle, true);
+            cleanAppRestrictions(userHandle);
         }
         if (unblockApps) {
             unblockAllAppsForUser(userHandle);
@@ -1691,12 +1689,6 @@
                 user.lastLoggedInTime = now;
                 writeUserLocked(user);
             }
-            // If this is not a restricted profile and there is no restrictions pin, clean up
-            // all restrictions files that might have been left behind, else clean up just the
-            // ones with uninstalled packages
-            RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
-            final long salt = pinState == null ? 0 : pinState.salt;
-            cleanAppRestrictions(userId, (!user.isRestricted() && salt == 0));
         }
     }
 
@@ -1772,17 +1764,4 @@
             }
         }
     }
-
-    private PackageMonitor mUserPackageMonitor = new PackageMonitor() {
-        @Override
-        public void onPackageRemoved(String pkg, int uid) {
-            final int userId = this.getChangingUserId();
-            // Package could be disappearing because it is being blocked, so also check if
-            // it has been uninstalled.
-            final boolean uninstalled = isPackageDisappearing(pkg) == PACKAGE_PERMANENT_CHANGE;
-            if (uninstalled && userId >= 0 && !isPackageInstalled(pkg, userId)) {
-                cleanAppRestrictionsForPackage(pkg, userId);
-            }
-        }
-    };
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
new file mode 100644
index 0000000..60724e7
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 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.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.
+ * @hide
+ */
+public class WebViewUpdateService extends IWebViewUpdateService.Stub {
+
+    private static final String TAG = "WebViewUpdateService";
+
+    private boolean mRelroReady32Bit = false;
+    private boolean mRelroReady64Bit = false;
+
+    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);
+    }
+
+    /**
+     * The shared relro process calls this to notify us that it's done trying to create a relro
+     * file.
+     */
+    public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
+        // Verify that the caller is the shared relro process.
+        if (Binder.getCallingUid() != Process.SHARED_RELRO_UID) {
+            return;
+        }
+
+        synchronized (this) {
+            if (is64Bit) {
+                mRelroReady64Bit = true;
+            } else {
+                mRelroReady32Bit = true;
+            }
+            this.notifyAll();
+        }
+    }
+
+    /**
+     * WebViewFactory calls this to block WebView loading until the relro file is created.
+     */
+    public void waitForRelroCreationCompleted(boolean is64Bit) {
+        synchronized (this) {
+            if (is64Bit) {
+                while (!mRelroReady64Bit) {
+                    try {
+                        this.wait();
+                    } catch (InterruptedException e) {}
+                }
+            } else {
+                while (!mRelroReady32Bit) {
+                    try {
+                        this.wait();
+                    } catch (InterruptedException e) {}
+                }
+            }
+        }
+    }
+
+    private void onWebViewUpdateInstalled() {
+        Log.d(TAG, "WebView Package updated!");
+
+        synchronized (this) {
+            mRelroReady32Bit = false;
+            mRelroReady64Bit = false;
+        }
+        WebViewFactory.prepareWebViewInSystemServer();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a5959d4..fa22ded 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -11173,17 +11173,17 @@
         public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
             synchronized (mWindowMap) {
                 mWaitingForDrawnCallback = callback;
-                final WindowList windows = getDefaultWindowListLocked();
-                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                    final WindowState win = windows.get(winNdx);
-                    if (win.mHasSurface && win.isWinVisibleLw()) {
-                        if (!win.mIsWallpaper) {
-                            // Don't force wallpaper to redraw.
+                for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+                    final WindowList windows =
+                            mDisplayContents.valueAt(displayNdx).getWindowList();
+                    for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                        final WindowState win = windows.get(winNdx);
+                        if (win.mHasSurface && win.isWinVisibleLw() && !win.mIsWallpaper) {
                             win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+                            // Force add to mResizingWindows.
+                            win.mLastContentInsets.set(-1, -1, -1, -1);
+                            mWaitingForDrawn.add(win);
                         }
-                        // Force add to mResizingWindows.
-                        win.mLastContentInsets.set(-1, -1, -1, -1);
-                        mWaitingForDrawn.add(win);
                     }
                 }
                 requestTraversalLocked();
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 92d3d95..12ed920 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,7 +55,9 @@
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
 import android.net.ProxyInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -136,6 +138,7 @@
     private static final boolean DBG = false;
 
     private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
+    private static final String ATTR_SETUP_COMPLETE = "setup-complete";
 
     final Context mContext;
     final UserManager mUserManager;
@@ -174,6 +177,7 @@
             }
         }
     }
+
     public static class DevicePolicyData {
         int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         int mActivePasswordLength = 0;
@@ -188,6 +192,7 @@
         int mUserHandle;
         int mPasswordOwner = -1;
         long mLastMaximumTimeToLock = -1;
+        boolean mUserSetupComplete = false;
 
         final HashMap<ComponentName, ActiveAdmin> mAdminMap
                 = new HashMap<ComponentName, ActiveAdmin>();
@@ -252,6 +257,7 @@
     static class ActiveAdmin {
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
+        private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
         private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
         private static final String TAG_ACCOUNT_TYPE = "account-type";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
@@ -319,6 +325,7 @@
 
         boolean encryptionRequested = false;
         boolean disableCamera = false;
+        boolean disableCallerId = false;
         Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // TODO: review implementation decisions with frameworks team
@@ -431,6 +438,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
                 out.endTag(null, TAG_DISABLE_CAMERA);
             }
+            if (disableCallerId) {
+                out.startTag(null, TAG_DISABLE_CALLER_ID);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
+                out.endTag(null, TAG_DISABLE_CALLER_ID);
+            }
             if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
                 out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
                 out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -513,6 +525,9 @@
                 } else if (TAG_DISABLE_CAMERA.equals(tag)) {
                     disableCamera = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
+                    disableCallerId = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
                     disabledKeyguardFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -589,6 +604,8 @@
                     pw.println(encryptionRequested);
             pw.print(prefix); pw.print("disableCamera=");
                     pw.println(disableCamera);
+            pw.print(prefix); pw.print("disableCallerId=");
+                    pw.println(disableCallerId);
             pw.print(prefix); pw.print("disabledKeyguardFeatures=");
                     pw.println(disabledKeyguardFeatures);
         }
@@ -973,6 +990,10 @@
                 out.attribute(null, ATTR_PERMISSION_PROVIDER,
                         policy.mRestrictionsProvider.flattenToString());
             }
+            if (policy.mUserSetupComplete) {
+                out.attribute(null, ATTR_SETUP_COMPLETE,
+                        Boolean.toString(true));
+            }
 
             final int N = policy.mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1074,6 +1095,10 @@
             if (permissionProvider != null) {
                 policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider);
             }
+            String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
+            if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
+                policy.mUserSetupComplete = true;
+            }
 
             type = parser.next();
             int outerDepth = parser.getDepth();
@@ -1266,6 +1291,10 @@
                 }
             }
         }
+        // Register an observer for watching for user setup complete.
+        new SetupContentObserver(mHandler).register(mContext.getContentResolver());
+        // Initialize the user setup state, to handle the upgrade case.
+        updateUserSetupComplete();
     }
 
     private void cleanUpOldUsers() {
@@ -3274,6 +3303,25 @@
         return null;
     }
 
+    // Returns the active profile owner for this user or null if the current user has no
+    // profile owner.
+    private ActiveAdmin getProfileOwnerAdmin(int userHandle) {
+        String profileOwnerPackage = getProfileOwner(userHandle);
+        if (profileOwnerPackage == null) {
+            return null;
+        }
+
+        DevicePolicyData policy = getUserData(userHandle);
+        final int n = policy.mAdminList.size();
+        for (int i = 0; i < n; i++) {
+            ActiveAdmin admin = policy.mAdminList.get(i);
+            if (profileOwnerPackage.equals(admin.info.getPackageName())) {
+                return admin;
+            }
+        }
+        return null;
+    }
+
     @Override
     public String getProfileOwnerName(int userHandle) {
         if (!mHasFeature) {
@@ -3312,6 +3360,12 @@
         }
     }
 
+    private void enforceSystemProcess(String message) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException(message);
+        }
+    }
+
     private void enforceNotManagedProfile(int userHandle, String message) {
         if(isManagedProfile(userHandle)) {
             throw new SecurityException("You can not " + message + " for a managed profile. ");
@@ -3871,11 +3925,17 @@
 
     @Override
     public String[] getAccountTypesWithManagementDisabled() {
+        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
+        enforceCrossUserPermission(userId);
         if (!mHasFeature) {
             return null;
         }
         synchronized (this) {
-            DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
+            DevicePolicyData policy = getUserData(userId);
             final int N = policy.mAdminList.size();
             HashSet<String> resultSet = new HashSet<String>();
             for (int i = 0; i < N; i++) {
@@ -3933,6 +3993,50 @@
         return false;
     }
 
+    @Override
+    public void setCrossProfileCallerIdDisabled(ComponentName who, boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (admin.disableCallerId != disabled) {
+                admin.disableCallerId = disabled;
+                saveSettingsLocked(UserHandle.getCallingUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileCallerIdDisabled(ComponentName who) {
+        if (!mHasFeature) {
+            return false;
+        }
+
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return admin.disableCallerId;
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
+        enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+        synchronized (this) {
+            ActiveAdmin admin = getProfileOwnerAdmin(userId);
+            return (admin != null) ? admin.disableCallerId : false;
+        }
+    }
+
     /**
      * Sets which packages may enter lock task mode.
      *
@@ -4102,4 +4206,50 @@
             return audioManager.isMasterMute();
         }
     }
+
+    /**
+     * We need to update the internal state of whether a user has completed setup once. After
+     * that, we ignore any changes that reset the Settings.Secure.USER_SETUP_COMPLETE changes
+     * as we don't trust any apps that might try to reset it.
+     * <p>
+     * Unfortunately, we don't know which user's setup state was changed, so we write all of
+     * them.
+     */
+    void updateUserSetupComplete() {
+        List<UserInfo> users = mUserManager.getUsers(true);
+        ContentResolver resolver = mContext.getContentResolver();
+        final int N = users.size();
+        for (int i = 0; i < N; i++) {
+            int userHandle = users.get(i).id;
+            if (Settings.Secure.getIntForUser(resolver, Settings.Secure.USER_SETUP_COMPLETE, 0,
+                    userHandle) != 0) {
+                DevicePolicyData policy = getUserData(userHandle);
+                policy.mUserSetupComplete = true;
+                synchronized (this) {
+                    saveSettingsLocked(userHandle);
+                }
+            }
+        }
+    }
+
+    private class SetupContentObserver extends ContentObserver {
+
+        private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
+                Settings.Secure.USER_SETUP_COMPLETE);
+
+        public SetupContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        void register(ContentResolver resolver) {
+            resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mUserSetupComplete.equals(uri)) {
+                updateUserSetupComplete();
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8d38827..de929cb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -49,6 +49,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.WindowManager;
+import android.webkit.WebViewFactory;
 
 import com.android.internal.R;
 import com.android.internal.os.BinderInternal;
@@ -71,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;
@@ -91,6 +93,7 @@
 import com.android.server.twilight.TwilightService;
 import com.android.server.usb.UsbService;
 import com.android.server.wallpaper.WallpaperManagerService;
+import com.android.server.webkit.WebViewUpdateService;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
@@ -408,6 +411,12 @@
             Slog.i(TAG, "Reading configuration...");
             SystemConfig.getInstance();
 
+            Slog.i(TAG, "WebView Update Service");
+            ServiceManager.addService("webviewupdate", new WebViewUpdateService(context));
+
+            Slog.i(TAG, "WebViewFactory preparation");
+            WebViewFactory.prepareWebViewInSystemServer();
+
             Slog.i(TAG, "Scheduling Policy");
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
 
@@ -941,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 548e7d3..bed85fc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.Keyphrase;
@@ -75,7 +74,7 @@
             + SoundModelContract.KEY_TYPE + " INTEGER,"
             + SoundModelContract.KEY_DATA + " BLOB" + ")";
 
-    public DatabaseHelper(Context context, CursorFactory factory) {
+    public DatabaseHelper(Context context) {
         super(context, NAME, null, VERSION);
     }
 
@@ -94,40 +93,62 @@
         onCreate(db);
     }
 
-    /**
-     * TODO: Change to addOrUpdate to handle changes here.
-     */
-    public void addKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
-        SQLiteDatabase db = this.getWritableDatabase();
+    public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
+        SQLiteDatabase db = getWritableDatabase();
         ContentValues values = new ContentValues();
         // Generate a random ID for the model.
         values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
         values.put(SoundModelContract.KEY_DATA, soundModel.data);
         values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
 
-        if (db.insert(SoundModelContract.TABLE, null, values) != -1) {
+        boolean status = true;
+        if (db.insertWithOnConflict(
+                SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
             for (Keyphrase keyphrase : soundModel.keyphrases) {
-                addKeyphrase(soundModel.uuid, keyphrase);
+                status &= addOrUpdateKeyphrase(soundModel.uuid, keyphrase);
             }
+            return status;
         } else {
             Slog.w(TAG, "Failed to persist sound model to database");
+            return false;
+        }
+    }
+
+    private boolean addOrUpdateKeyphrase(UUID modelId, Keyphrase keyphrase) {
+        SQLiteDatabase db = getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(KeyphraseContract.KEY_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
+        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
+        values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
+        values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
+        if (db.insertWithOnConflict(
+                KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
+            return true;
+        } else {
+            Slog.w(TAG, "Failed to persist keyphrase to database");
+            return false;
         }
     }
 
     /**
-     * TODO(sansid): Change to addOrUpdate to handle changes here.
+     * Deletes the sound model and associated keyphrases.
      */
-    private void addKeyphrase(UUID modelId, Keyphrase keyphrase) {
-        SQLiteDatabase db = this.getWritableDatabase();
-        ContentValues values = new ContentValues();
-        values.put(KeyphraseContract.KEY_ID, keyphrase.id);
-        values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
-        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
-        values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
-        values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
-        if (db.insert(KeyphraseContract.TABLE, null, values) == -1) {
-            Slog.w(TAG, "Failed to persist keyphrase to database");
+    public boolean deleteKeyphraseSoundModel(UUID uuid) {
+        SQLiteDatabase db = getWritableDatabase();
+        String modelId = uuid.toString();
+        String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
+        boolean status = true;
+        if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
+            Slog.w(TAG, "No sound models deleted from the database");
+            status = false;
         }
+        String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
+        if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
+            Slog.w(TAG, "No keyphrases deleted from the database");
+            status = false;
+        }
+        return status;
     }
 
     /**
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 85042f7..63702ba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -62,11 +62,13 @@
 
     final Context mContext;
     final ContentResolver mResolver;
+    final DatabaseHelper mDbHelper;
 
     public VoiceInteractionManagerService(Context context) {
         super(context);
         mContext = context;
         mResolver = context.getContentResolver();
+        mDbHelper = new DatabaseHelper(context);
     }
 
     @Override
@@ -264,8 +266,7 @@
 
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    // TODO: Add the implementation here.
-                    return null;
+                    return mDbHelper.getKephraseSoundModels();
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -280,10 +281,26 @@
                     throw new SecurityException("Caller does not hold the permission "
                             + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
                 }
+                if (model == null) {
+                    throw new IllegalArgumentException("Model must not be null");
+                }
+
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    // TODO: Add the implementation here.
-                    return VoiceInteractionManagerService.STATUS_ERROR;
+                    // If the keyphrases are not present in the model, delete the model.
+                    if (model.keyphrases == null) {
+                        if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
+                            return STATUS_OK;
+                        } else {
+                            return STATUS_ERROR;
+                        }
+                    } else {
+                        if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
+                            return STATUS_OK;
+                        } else {
+                            return STATUS_ERROR;
+                        }
+                    }
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 1f40c26..54af5d4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -87,6 +87,10 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             synchronized (mLock) {
                 mService = IVoiceInteractionService.Stub.asInterface(service);
+                try {
+                    mService.ready();
+                } catch (RemoteException e) {
+                }
             }
         }
 
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index 9f38d0d..930336f 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -62,6 +62,11 @@
      */
     public static final int STATE_DISCONNECTED = 7;
 
+    /**
+     * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+     */
+    public static final int STATE_PRE_DIAL_WAIT = 8;
+
     public static class Details {
         private final Uri mHandle;
         private final int mHandlePresentation;
@@ -74,6 +79,7 @@
         private final long mConnectTimeMillis;
         private final GatewayInfo mGatewayInfo;
         private final int mVideoState;
+        private final StatusHints mStatusHints;
 
         /**
          * @return The handle (e.g., phone number) to which the {@code Call} is currently
@@ -160,6 +166,13 @@
             return mVideoState;
         }
 
+        /*
+         * @return The current {@link android.telecomm.StatusHints}, or null if none has been set.
+         */
+        public StatusHints getStatusHints() {
+            return mStatusHints;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -176,7 +189,8 @@
                         Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) &&
                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
                         Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
-                        Objects.equals(mVideoState, d.mVideoState);
+                        Objects.equals(mVideoState, d.mVideoState) &&
+                        Objects.equals(mStatusHints, d.mStatusHints);
             }
             return false;
         }
@@ -194,7 +208,8 @@
                     Objects.hashCode(mDisconnectCauseMsg) +
                     Objects.hashCode(mConnectTimeMillis) +
                     Objects.hashCode(mGatewayInfo) +
-                    Objects.hashCode(mVideoState);
+                    Objects.hashCode(mVideoState) +
+                    Objects.hashCode(mStatusHints);
         }
 
         /** {@hide} */
@@ -209,7 +224,8 @@
                 String disconnectCauseMsg,
                 long connectTimeMillis,
                 GatewayInfo gatewayInfo,
-                int videoState) {
+                int videoState,
+                StatusHints statusHints) {
             mHandle = handle;
             mHandlePresentation = handlePresentation;
             mCallerDisplayName = callerDisplayName;
@@ -221,6 +237,7 @@
             mConnectTimeMillis = connectTimeMillis;
             mGatewayInfo = gatewayInfo;
             mVideoState = videoState;
+            mStatusHints = statusHints;
         }
     }
 
@@ -337,9 +354,10 @@
 
     /**
      * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
+     * @param videoState The video state in which to answer the call.
      */
-    public void answer() {
-        mInCallAdapter.answerCall(mTelecommCallId);
+    public void answer(int videoState) {
+        mInCallAdapter.answerCall(mTelecommCallId, videoState);
     }
 
     /**
@@ -430,6 +448,15 @@
     }
 
     /**
+     * Notifies this {@code Call} the a {@code PhoneAccount} has been selected and to proceed
+     * with placing an outgoing call.
+     */
+    public void phoneAccountSelected(PhoneAccount account) {
+        mInCallAdapter.phoneAccountSelected(mTelecommCallId, account);
+
+    }
+
+    /**
      * Instructs this {@code Call} to enter a conference.
      */
     public void conference() {
@@ -558,7 +585,8 @@
                 inCallCall.getDisconnectCauseMsg(),
                 inCallCall.getConnectTimeMillis(),
                 inCallCall.getGatewayInfo(),
-                inCallCall.getVideoState());
+                inCallCall.getVideoState(),
+                inCallCall.getStatusHints());
         boolean detailsChanged = !Objects.equals(mDetails, details);
         if (detailsChanged) {
             mDetails = details;
@@ -703,6 +731,8 @@
         switch (inCallCallState) {
             case NEW:
                 return STATE_NEW;
+            case PRE_DIAL_WAIT:
+                return STATE_PRE_DIAL_WAIT;
             case DIALING:
                 return STATE_DIALING;
             case RINGING:
diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java
index a464da5..7abf4ab 100644
--- a/telecomm/java/android/telecomm/CallState.java
+++ b/telecomm/java/android/telecomm/CallState.java
@@ -32,6 +32,14 @@
     NEW,
 
     /**
+     * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
+     * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
+     * this is the state where the InCallUI is waiting for the user to select a {@link PhoneAccount}
+     * to call from.
+     */
+    PRE_DIAL_WAIT,
+
+    /**
      * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
      * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
      * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index db834a4..130364f 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -486,8 +486,10 @@
     /**
      * Notifies this Connection, which is in {@link State#RINGING}, of
      * a request to accept.
+     *
+     * @param videoState The video state in which to answer the call.
      */
-    protected void onAnswer() {}
+    protected void onAnswer(int videoState) {}
 
     /**
      * Notifies this Connection, which is in {@link State#RINGING}, of
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 2a6804b..5855470 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -121,8 +121,11 @@
         }
 
         @Override
-        public void answer(String callId) {
-            mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
+        public void answer(String callId, int videoState) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.arg2 = videoState;
+            mHandler.obtainMessage(MSG_ANSWER, args).sendToTarget();
         }
 
         @Override
@@ -209,9 +212,17 @@
                 case MSG_ABORT:
                     abort((String) msg.obj);
                     break;
-                case MSG_ANSWER:
-                    answer((String) msg.obj);
+                case MSG_ANSWER: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        String callId = (String) args.arg1;
+                        int videoState = (int) args.arg2;
+                        answer(callId, videoState);
+                    } finally {
+                        args.recycle();
+                    }
                     break;
+                }
                 case MSG_REJECT:
                     reject((String) msg.obj);
                     break;
@@ -428,9 +439,9 @@
         findConnectionForAction(callId, "abort").onAbort();
     }
 
-    private void answer(String callId) {
+    private void answer(String callId, int videoState) {
         Log.d(this, "answer %s", callId);
-        findConnectionForAction(callId, "answer").onAnswer();
+        findConnectionForAction(callId, "answer").onAnswer(videoState);
     }
 
     private void reject(String callId) {
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index 66cf1df..c872c58 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -45,10 +45,11 @@
      * Instructs Telecomm to answer the specified call.
      *
      * @param callId The identifier of the call to answer.
+     * @param videoState The video state in which to answer the call.
      */
-    public void answerCall(String callId) {
+    public void answerCall(String callId, int videoState) {
         try {
-            mAdapter.answerCall(callId);
+            mAdapter.answerCall(callId, videoState);
         } catch (RemoteException e) {
         }
     }
@@ -200,6 +201,19 @@
     }
 
     /**
+     * Instructs Telecomm to add a PhoneAccount to the specified call
+     *
+     * @param callId The identifier of the call
+     * @param account The PhoneAccount through which to place the call
+     */
+    public void phoneAccountSelected(String callId, PhoneAccount account) {
+        try {
+            mAdapter.phoneAccountSelected(callId, account);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Instructs Telecomm to conference the specified call.
      *
      * @param callId The unique ID of the call.
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 028b6e4..27d92b2 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -56,7 +56,7 @@
                 case MSG_UPDATE_CALL:
                     mPhone.internalUpdateCall((InCallCall) msg.obj);
                     break;
-                                case MSG_SET_POST_DIAL: {
+                case MSG_SET_POST_DIAL: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         String callId = (String) args.arg1;
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index 3475305..5d8579e 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -129,10 +129,10 @@
         }
     }
 
-    public void answer() {
+    public void answer(int videoState) {
         try {
             if (mConnected) {
-                mConnectionService.answer(mConnectionId);
+                mConnectionService.answer(mConnectionId, videoState);
             }
         } catch (RemoteException ignored) {
         }
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index b50c1d7..0ede387 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -111,6 +111,16 @@
             "android.telecomm.extra.CONNECTION_SERVICE";
 
     /**
+     * The number which emergency services will use to return calls, if necessary. The in-call UI
+     * will take care of displaying this to the user.
+     * <p>
+     * Note that this should only be populated for emergency numbers, and if the number emergency
+     * services will see is different than the user's known phone number.
+     */
+    public static final String EXTRA_EMERGENCY_CALL_BACK_NUMBER =
+            "android.telecomm.extra.EMERGENCY_CALL_BACK_NUMBER";
+
+    /**
      * The dual tone multi-frequency signaling character sent to indicate the dialing system should
      * pause for a predefined period.
      */
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index 9360219..05375c9 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -36,7 +36,7 @@
 
     void abort(String callId);
 
-    void answer(String callId);
+    void answer(String callId, int videoState);
 
     void reject(String callId);
 
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index ce0309f..aca8c0a 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telecomm;
 
 import android.telecomm.CallAudioState;
+import android.telecomm.PhoneAccount;
 
 /**
  * Internal remote callback interface for in-call services.
@@ -26,7 +27,7 @@
  * {@hide}
  */
 oneway interface IInCallAdapter {
-    void answerCall(String callId);
+    void answerCall(String callId, int videoState);
 
     void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
 
@@ -48,6 +49,8 @@
 
     void phoneAccountClicked(String callId);
 
+    void phoneAccountSelected(String callId, in PhoneAccount account);
+
     void conference(String callId);
 
     void splitFromConference(String callId);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5e6cb14..6e1ee78 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -24,7 +24,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.telecomm.PhoneAccount;
 import android.util.Log;
 
 import com.android.internal.telecomm.ITelecommService;
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 56fa0a9..a16b749 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -22,7 +22,8 @@
 
     <application
         android:hardwareAccelerated="true"
-        android:label="vector" >
+        android:label="vector"
+        android:supportsRtl="true" >
         <activity
             android:name="VectorDrawablePerformance"
             android:label="Vector Performance" >
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
index 5b4c4ab..2fdb676 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true" >
 
     <size
         android:height="64dp"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
index 90694fb..296e0261 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
@@ -12,7 +12,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android">
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
 
     <size
             android:width="64dp"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index c6595fa..1633326 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
 
     <size
         android:height="64dp"
diff --git a/tests/VoiceEnrollment/res/xml/enrollment_application.xml b/tests/VoiceEnrollment/res/xml/enrollment_application.xml
index 710a0ac..70a6e0c 100644
--- a/tests/VoiceEnrollment/res/xml/enrollment_application.xml
+++ b/tests/VoiceEnrollment/res/xml/enrollment_application.xml
@@ -20,4 +20,5 @@
 <voice-enrollment-application xmlns:android="http://schemas.android.com/apk/res/android"
     android:searchKeyphraseId="101"
     android:searchKeyphrase="Hello There"
-    android:searchKeyphraseSupportedLocales="en-US,en-GB,fr-FR,de-DE" />
+    android:searchKeyphraseSupportedLocales="en-US,en-GB,fr-FR,de-DE"
+    android:searchKeyphraseRecognitionFlags="voiceTrigger" />
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index db43be3..ab2e8ac 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -27,8 +27,8 @@
     static final String TAG = "MainInteractionService";
 
     @Override
-    public void onCreate() {
-        super.onCreate();
+    public void onReady() {
+        super.onReady();
         Log.i(TAG, "Creating " + this);
         Log.i(TAG, "Keyphrase enrollment error? " + getKeyphraseEnrollmentInfo().getParseError());
         Log.i(TAG, "Keyphrase enrollment meta-data: "
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;
     }