merge in lmp-release history after reset to a792fc8a027d84e91879b9204c901ee0fc3eaee8
diff --git a/Android.mk b/Android.mk
index 0d02be7..7dfa6a0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -322,15 +322,17 @@
 	media/java/android/media/IRemoteVolumeObserver.aidl \
 	media/java/android/media/IRingtonePlayer.aidl \
 	media/java/android/media/IVolumeController.aidl \
+	media/java/android/media/browse/IMediaBrowserService.aidl \
+	media/java/android/media/browse/IMediaBrowserServiceCallbacks.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/routing/IMediaRouteService.aidl \
 	media/java/android/media/routing/IMediaRouteClientCallback.aidl \
 	media/java/android/media/routing/IMediaRouter.aidl \
 	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 \
@@ -391,76 +393,27 @@
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext
 
-LOCAL_MODULE := framework-base
+LOCAL_MODULE := framework
+
+LOCAL_DX_FLAGS := --core-library --multi-dex
 
 LOCAL_RMTYPEDEFS := true
 
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Make sure that R.java and Manifest.java are built before we build
-# the source for this library.
-framework_res_R_stamp := \
-	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
-$(full_classes_compiled_jar): $(framework_res_R_stamp)
-
-# Build part 1 of the framework library.
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := framework
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := framework-base
-LOCAL_DX_FLAGS := --core-library
-
-# List of packages to include along with their descendants.
-LOCAL_JAR_PACKAGES := \
-    android
-
-# List of packages to exclude along with their descendants.
-# Overrides inclusion.
-LOCAL_JAR_EXCLUDE_PACKAGES := \
-    android.bluetooth \
-    android.filterfw \
-    android.filterpacks \
-    android.hardware \
-    android.telephony
-
 # List of classes and interfaces which should be loaded by the Zygote.
 LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
 
 include $(BUILD_JAVA_LIBRARY)
 framework_module := $(LOCAL_INSTALLED_MODULE)
 
-# Build part 2 of the framework library.
-# ============================================================
-include $(CLEAR_VARS)
+# Make sure that R.java and Manifest.java are built before we build
+# the source for this library.
+framework_res_R_stamp := \
+	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
+$(full_classes_compiled_jar): $(framework_res_R_stamp)
 
-LOCAL_MODULE := framework2
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := framework-base
-LOCAL_DX_FLAGS := --core-library
-
-# List of packages to include along with their descendants.
-LOCAL_JAR_PACKAGES := \
-    android.bluetooth \
-    android.filterfw \
-    android.filterpacks \
-    android.hardware \
-    android.telephony \
-    com \
-    javax
-
-include $(BUILD_JAVA_LIBRARY)
-framework2_module := $(LOCAL_INSTALLED_MODULE)
-
-# Make sure that all framework modules are installed when framework is.
-# ============================================================
 $(framework_module): | $(dir $(framework_module))framework-res.apk
-$(framework_module): | $(dir $(framework_module))framework2.jar
 
-framework_built := $(call java-lib-deps,framework framework2)
+framework_built := $(call java-lib-deps,framework)
 
 # Copy AIDL files to be preprocessed and included in the SDK,
 # specified relative to the root of the build tree.
@@ -495,6 +448,7 @@
 	frameworks/base/core/java/android/os/DropBoxManager.aidl \
 	frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
 	frameworks/base/core/java/android/os/ParcelUuid.aidl \
+        frameworks/base/core/java/android/os/PersistableBundle.aidl \
 	frameworks/base/core/java/android/print/PrinterInfo.aidl \
 	frameworks/base/core/java/android/print/PageRange.aidl \
 	frameworks/base/core/java/android/print/PrintAttributes.aidl \
@@ -636,7 +590,6 @@
 	okhttp \
 	ext \
 	framework \
-	framework2 \
 	telephony-common \
 	voip-common
 
@@ -678,7 +631,7 @@
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework-base,,COMMON)
+	$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
 
 framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
 	$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 2c9ea54..3014e0f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -210,6 +210,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework2_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/telecomm/java/com/android/internal/telecomm)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework.* $(PRODUCT_OUT)/system/framework2.*)
 
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index d1a39e7..ce5e366 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -380,6 +380,8 @@
     field public static final int centerY = 16843171; // 0x10101a3
     field public static final int checkBoxPreferenceStyle = 16842895; // 0x101008f
     field public static final int checkMark = 16843016; // 0x1010108
+    field public static final int checkMarkTint = 16843949; // 0x10104ad
+    field public static final int checkMarkTintMode = 16843950; // 0x10104ae
     field public static final int checkable = 16843237; // 0x10101e5
     field public static final int checkableBehavior = 16843232; // 0x10101e0
     field public static final int checkboxStyle = 16842860; // 0x101006c
@@ -744,6 +746,7 @@
     field public static final int l_resource_pad23 = 16843770; // 0x10103fa
     field public static final int l_resource_pad24 = 16843769; // 0x10103f9
     field public static final int l_resource_pad25 = 16843768; // 0x10103f8
+    field public static final int l_resource_pad26 = 16843767; // 0x10103f7
     field public static final int l_resource_pad3 = 16843790; // 0x101040e
     field public static final int l_resource_pad4 = 16843789; // 0x101040d
     field public static final int l_resource_pad5 = 16843788; // 0x101040c
@@ -1327,7 +1330,6 @@
     field public static final int trimPathEnd = 16843813; // 0x1010425
     field public static final int trimPathOffset = 16843814; // 0x1010426
     field public static final int trimPathStart = 16843812; // 0x1010424
-    field public static final int tvInputType = 16843767; // 0x10103f7
     field public static final int type = 16843169; // 0x10101a1
     field public static final int typeface = 16842902; // 0x1010096
     field public static final int uiOptions = 16843672; // 0x1010398
@@ -3818,6 +3820,7 @@
     method public void startWatchingMode(java.lang.String, java.lang.String, android.app.AppOpsManager.OnOpChangedListener);
     method public void stopWatchingMode(android.app.AppOpsManager.OnOpChangedListener);
     field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
     field public static final int MODE_ERRORED = 2; // 0x2
     field public static final int MODE_IGNORED = 1; // 0x1
     field public static final java.lang.String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -5355,7 +5358,7 @@
     method public boolean installCaCert(android.content.ComponentName, byte[]);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
-    method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
+    method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5365,9 +5368,9 @@
     method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
-    method public boolean setApplicationBlocked(android.content.ComponentName, java.lang.String, boolean);
+    method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
-    method public int setApplicationsBlocked(android.content.ComponentName, android.content.Intent, boolean);
+    method public int setApplicationsHidden(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);
@@ -6660,7 +6663,7 @@
   public abstract class AbstractRestrictionsProvider extends android.content.BroadcastReceiver {
     ctor public AbstractRestrictionsProvider();
     method public void onReceive(android.content.Context, android.content.Intent);
-    method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle);
+    method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, java.lang.String, android.os.PersistableBundle);
   }
 
   public abstract class AbstractThreadedSyncAdapter {
@@ -8050,14 +8053,17 @@
 
   public class RestrictionsManager {
     method public android.os.Bundle getApplicationRestrictions();
+    method public android.content.Intent getLocalApprovalIntent();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
     method public boolean hasRestrictionsProvider();
-    method public void notifyPermissionResponse(java.lang.String, android.os.Bundle);
-    method public void requestPermission(java.lang.String, android.os.Bundle);
-    field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+    method public void notifyPermissionResponse(java.lang.String, android.os.PersistableBundle);
+    method public void requestPermission(java.lang.String, java.lang.String, android.os.PersistableBundle);
+    field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.content.action.PERMISSION_RESPONSE_RECEIVED";
+    field public static final java.lang.String ACTION_REQUEST_LOCAL_APPROVAL = "android.content.action.REQUEST_LOCAL_APPROVAL";
     field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.content.action.REQUEST_PERMISSION";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
+    field public static final java.lang.String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID";
     field public static final java.lang.String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
     field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
     field public static final java.lang.String META_DATA_APP_RESTRICTIONS = "android.content.APP_RESTRICTIONS";
@@ -8070,7 +8076,6 @@
     field public static final java.lang.String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
     field public static final java.lang.String REQUEST_KEY_TITLE = "android.request.title";
     field public static final java.lang.String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
-    field public static final java.lang.String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
     field public static final java.lang.String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
     field public static final java.lang.String RESPONSE_KEY_MESSAGE = "android.response.msg";
     field public static final java.lang.String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
@@ -8278,7 +8283,6 @@
     field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
     field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
     field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
-    field public static final int DO_NOT_PERSIST = 1; // 0x1
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
     field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
     field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
@@ -8298,6 +8302,7 @@
     field public static final int LAUNCH_SINGLE_TASK = 2; // 0x2
     field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1
     field public static final int PERSIST_ACROSS_REBOOTS = 2; // 0x2
+    field public static final int PERSIST_NEVER = 1; // 0x1
     field public static final int PERSIST_ROOT_ONLY = 0; // 0x0
     field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3
     field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa
@@ -8873,6 +8878,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int FLAG_COSTS_MONEY = 1; // 0x1
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
+    field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
     field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
     field public static final int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -14972,7 +14978,14 @@
     method public final void setInteger(java.lang.String, int);
     method public final void setLong(java.lang.String, long);
     method public final void setString(java.lang.String, java.lang.String);
+    field public static final java.lang.String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
+    field public static final java.lang.String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
+    field public static final java.lang.String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
+    field public static final java.lang.String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level";
+    field public static final java.lang.String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level";
+    field public static final java.lang.String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
     field public static final java.lang.String KEY_AAC_PROFILE = "aac-profile";
+    field public static final java.lang.String KEY_AAC_SBR_MODE = "aac-sbr-mode";
     field public static final java.lang.String KEY_BITRATE_MODE = "bitrate-mode";
     field public static final java.lang.String KEY_BIT_RATE = "bitrate";
     field public static final java.lang.String KEY_CHANNEL_COUNT = "channel-count";
@@ -15070,7 +15083,7 @@
     method public android.media.MediaMetadata.Builder putString(java.lang.String, java.lang.String);
   }
 
-  public abstract class MediaMetadataEditor {
+  public abstract deprecated class MediaMetadataEditor {
     method public synchronized void addEditableKey(int);
     method public abstract void apply();
     method public synchronized void clear();
@@ -15540,7 +15553,7 @@
     field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
   }
 
-  public class RemoteControlClient {
+  public deprecated class RemoteControlClient {
     ctor public RemoteControlClient(android.app.PendingIntent);
     ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
     method public android.media.RemoteControlClient.MetadataEditor editMetadata(boolean);
@@ -15572,7 +15585,7 @@
     field public static final int PLAYSTATE_STOPPED = 1; // 0x1
   }
 
-  public class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
+  public deprecated class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
     method public synchronized void apply();
     field public static final int BITMAP_KEY_ARTWORK = 100; // 0x64
   }
@@ -15589,7 +15602,7 @@
     method public abstract void onPlaybackPositionUpdate(long);
   }
 
-  public final class RemoteController {
+  public final deprecated class RemoteController {
     ctor public RemoteController(android.content.Context, android.media.RemoteController.OnClientUpdateListener) throws java.lang.IllegalArgumentException;
     ctor public RemoteController(android.content.Context, android.media.RemoteController.OnClientUpdateListener, android.os.Looper) throws java.lang.IllegalArgumentException;
     method public boolean clearArtworkConfiguration();
@@ -15668,7 +15681,7 @@
   }
 
   public class SoundPool {
-    ctor public SoundPool(int, int, int);
+    ctor public deprecated SoundPool(int, int, int);
     method public final void autoPause();
     method public final void autoResume();
     method public int load(java.lang.String, int);
@@ -15688,6 +15701,13 @@
     method public final boolean unload(int);
   }
 
+  public static class SoundPool.Builder {
+    ctor public SoundPool.Builder();
+    method public android.media.SoundPool build();
+    method public android.media.SoundPool.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
+    method public android.media.SoundPool.Builder setMaxStreams(int) throws java.lang.IllegalArgumentException;
+  }
+
   public static abstract interface SoundPool.OnLoadCompleteListener {
     method public abstract void onLoadComplete(android.media.SoundPool, int, int);
   }
@@ -16136,6 +16156,77 @@
 
 }
 
+package android.media.browse {
+
+  public final class MediaBrowser {
+    ctor public MediaBrowser(android.content.Context, android.content.ComponentName, android.media.browse.MediaBrowser.ConnectionCallback, android.os.Bundle);
+    method public void connect();
+    method public void disconnect();
+    method public android.net.Uri getRoot();
+    method public android.media.session.MediaSession.Token getSessionToken();
+    method public boolean isConnected();
+    method public void loadThumbnail(android.net.Uri, int, int, int, android.media.browse.MediaBrowser.ThumbnailCallback);
+    method public void subscribe(android.net.Uri, android.media.browse.MediaBrowser.SubscriptionCallback);
+    method public void unsubscribe(android.net.Uri);
+  }
+
+  public static class MediaBrowser.ConnectionCallback {
+    ctor public MediaBrowser.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public static abstract class MediaBrowser.SubscriptionCallback {
+    ctor public MediaBrowser.SubscriptionCallback();
+    method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowserItem>);
+    method public void onError(android.net.Uri);
+  }
+
+  public static abstract class MediaBrowser.ThumbnailCallback {
+    ctor public MediaBrowser.ThumbnailCallback();
+    method public void onError(android.net.Uri);
+    method public void onThumbnailLoaded(android.net.Uri, android.graphics.Bitmap);
+  }
+
+  public final class MediaBrowserItem implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public int getFlags();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public android.net.Uri getUri();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public static final class MediaBrowserItem.Builder {
+    ctor public MediaBrowserItem.Builder(android.net.Uri, int, java.lang.CharSequence);
+    method public android.media.browse.MediaBrowserItem build();
+    method public android.media.browse.MediaBrowserItem.Builder setExtras(android.os.Bundle);
+    method public android.media.browse.MediaBrowserItem.Builder setSummary(java.lang.CharSequence);
+  }
+
+  public abstract class MediaBrowserService extends android.app.Service {
+    ctor public MediaBrowserService();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.media.session.MediaSession.Token getSessionToken();
+    method public void notifyChange();
+    method public void notifyChildrenChanged(android.net.Uri);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.net.Uri onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract android.graphics.Bitmap onGetThumbnail(android.net.Uri, int, int, int);
+    method public abstract java.util.List<android.media.browse.MediaBrowserItem> onLoadChildren(android.net.Uri);
+    method public void setSessionToken(android.media.session.MediaSession.Token);
+    field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
+  }
+
+}
+
 package android.media.effect {
 
   public abstract class Effect {
@@ -16671,33 +16762,33 @@
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
     field public static final android.net.Uri CONTENT_URI;
-    field public static final int SERVICE_TYPE_AUDIO = 2; // 0x2
-    field public static final int SERVICE_TYPE_AUDIO_VIDEO = 1; // 0x1
-    field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
-    field public static final int TYPE_1SEG = 197632; // 0x30400
-    field public static final int TYPE_ATSC_C = 131584; // 0x20200
-    field public static final int TYPE_ATSC_M_H = 131840; // 0x20300
-    field public static final int TYPE_ATSC_T = 131072; // 0x20000
-    field public static final int TYPE_CMMB = 262400; // 0x40100
-    field public static final int TYPE_DTMB = 262144; // 0x40000
-    field public static final int TYPE_DVB_C = 66048; // 0x10200
-    field public static final int TYPE_DVB_C2 = 66049; // 0x10201
-    field public static final int TYPE_DVB_H = 66304; // 0x10300
-    field public static final int TYPE_DVB_S = 65792; // 0x10100
-    field public static final int TYPE_DVB_S2 = 65793; // 0x10101
-    field public static final int TYPE_DVB_SH = 66560; // 0x10400
-    field public static final int TYPE_DVB_T = 65536; // 0x10000
-    field public static final int TYPE_DVB_T2 = 65537; // 0x10001
-    field public static final int TYPE_ISDB_C = 197376; // 0x30300
-    field public static final int TYPE_ISDB_S = 197120; // 0x30200
-    field public static final int TYPE_ISDB_T = 196608; // 0x30000
-    field public static final int TYPE_ISDB_TB = 196864; // 0x30100
-    field public static final int TYPE_NTSC = 1; // 0x1
-    field public static final int TYPE_OTHER = 0; // 0x0
-    field public static final int TYPE_PAL = 2; // 0x2
-    field public static final int TYPE_SECAM = 3; // 0x3
-    field public static final int TYPE_S_DMB = 327936; // 0x50100
-    field public static final int TYPE_T_DMB = 327680; // 0x50000
+    field public static final java.lang.String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
+    field public static final java.lang.String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
+    field public static final java.lang.String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
+    field public static final java.lang.String TYPE_1SEG = "TYPE_1SEG";
+    field public static final java.lang.String TYPE_ATSC_C = "TYPE_ATSC_C";
+    field public static final java.lang.String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
+    field public static final java.lang.String TYPE_ATSC_T = "TYPE_ATSC_T";
+    field public static final java.lang.String TYPE_CMMB = "TYPE_CMMB";
+    field public static final java.lang.String TYPE_DTMB = "TYPE_DTMB";
+    field public static final java.lang.String TYPE_DVB_C = "TYPE_DVB_C";
+    field public static final java.lang.String TYPE_DVB_C2 = "TYPE_DVB_C2";
+    field public static final java.lang.String TYPE_DVB_H = "TYPE_DVB_H";
+    field public static final java.lang.String TYPE_DVB_S = "TYPE_DVB_S";
+    field public static final java.lang.String TYPE_DVB_S2 = "TYPE_DVB_S2";
+    field public static final java.lang.String TYPE_DVB_SH = "TYPE_DVB_SH";
+    field public static final java.lang.String TYPE_DVB_T = "TYPE_DVB_T";
+    field public static final java.lang.String TYPE_DVB_T2 = "TYPE_DVB_T2";
+    field public static final java.lang.String TYPE_ISDB_C = "TYPE_ISDB_C";
+    field public static final java.lang.String TYPE_ISDB_S = "TYPE_ISDB_S";
+    field public static final java.lang.String TYPE_ISDB_T = "TYPE_ISDB_T";
+    field public static final java.lang.String TYPE_ISDB_TB = "TYPE_ISDB_TB";
+    field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
+    field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
+    field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
+    field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
+    field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
     field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
     field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
     field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
@@ -16727,9 +16818,12 @@
     field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
     field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
     field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
     field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
     field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
     field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
@@ -16771,17 +16865,16 @@
     method public java.lang.CharSequence loadLabel(android.content.Context);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String EXTRA_INPUT_ID = "inputId";
-    field public static final int TYPE_COMPONENT = 6; // 0x6
-    field public static final int TYPE_COMPOSITE = 3; // 0x3
-    field public static final int TYPE_DISPLAY_PORT = 10; // 0xa
-    field public static final int TYPE_DVI = 8; // 0x8
-    field public static final int TYPE_HDMI = 9; // 0x9
-    field public static final int TYPE_OTHER_HARDWARE = 1; // 0x1
-    field public static final int TYPE_SCART = 5; // 0x5
-    field public static final int TYPE_SVIDEO = 4; // 0x4
-    field public static final int TYPE_TUNER = 2; // 0x2
-    field public static final int TYPE_VGA = 7; // 0x7
-    field public static final int TYPE_VIRTUAL = 0; // 0x0
+    field public static final int TYPE_COMPONENT = 1004; // 0x3ec
+    field public static final int TYPE_COMPOSITE = 1001; // 0x3e9
+    field public static final int TYPE_DISPLAY_PORT = 1008; // 0x3f0
+    field public static final int TYPE_DVI = 1006; // 0x3ee
+    field public static final int TYPE_HDMI = 1007; // 0x3ef
+    field public static final int TYPE_OTHER = 1000; // 0x3e8
+    field public static final int TYPE_SCART = 1003; // 0x3eb
+    field public static final int TYPE_SVIDEO = 1002; // 0x3ea
+    field public static final int TYPE_TUNER = 0; // 0x0
+    field public static final int TYPE_VGA = 1005; // 0x3ed
   }
 
   public final class TvInputManager {
@@ -28618,12 +28711,12 @@
     method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile);
     method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile);
     method public void setCallVideoClient(android.telecomm.CallVideoClient);
-    method public void setCamera(java.lang.String) throws android.os.RemoteException;
+    method public void setCamera(java.lang.String);
     method public void setDeviceOrientation(int);
     method public void setDisplaySurface(android.view.Surface);
     method public void setPauseImage(java.lang.String);
     method public void setPreviewSurface(android.view.Surface);
-    method public void setZoom(float) throws android.os.RemoteException;
+    method public void setZoom(float);
   }
 
   public final class RemoteConnection {
@@ -28699,6 +28792,7 @@
     method public void registerPhoneAccount(android.telecomm.PhoneAccount);
     method public void unregisterPhoneAccount(android.telecomm.PhoneAccountHandle);
     field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
+    field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecomm.intent.action.SHOW_CALL_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecomm.extra.CALL_BACK_NUMBER";
@@ -28711,6 +28805,7 @@
   }
 
   public class VideoCallProfile implements android.os.Parcelable {
+    ctor public VideoCallProfile(int);
     ctor public VideoCallProfile(int, int);
     method public int describeContents();
     method public int getQuality();
@@ -37620,9 +37715,13 @@
     ctor public CheckedTextView(android.content.Context, android.util.AttributeSet, int);
     ctor public CheckedTextView(android.content.Context, android.util.AttributeSet, int, int);
     method public android.graphics.drawable.Drawable getCheckMarkDrawable();
+    method public android.content.res.ColorStateList getCheckMarkTint();
+    method public android.graphics.PorterDuff.Mode getCheckMarkTintMode();
     method public boolean isChecked();
     method public void setCheckMarkDrawable(int);
     method public void setCheckMarkDrawable(android.graphics.drawable.Drawable);
+    method public void setCheckMarkTint(android.content.res.ColorStateList);
+    method public void setCheckMarkTintMode(android.graphics.PorterDuff.Mode);
     method public void setChecked(boolean);
     method public void toggle();
   }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d6c17ae..faf5622 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -197,13 +197,13 @@
             return;
         }
 
-        if ("block".equals(op)) {
-            runSetBlockedSetting(true);
+        if ("hide".equals(op)) {
+            runSetHiddenSetting(true);
             return;
         }
 
-        if ("unblock".equals(op)) {
-            runSetBlockedSetting(false);
+        if ("unhide".equals(op)) {
+            runSetHiddenSetting(false);
             return;
         }
 
@@ -1471,7 +1471,7 @@
         }
     }
 
-    private void runSetBlockedSetting(boolean state) {
+    private void runSetHiddenSetting(boolean state) {
         int userId = 0;
         String option = nextOption();
         if (option != null && option.equals("--user")) {
@@ -1492,9 +1492,9 @@
             return;
         }
         try {
-            mPm.setApplicationBlockedSettingAsUser(pkg, state, userId);
-            System.err.println("Package " + pkg + " new blocked state: "
-                    + mPm.getApplicationBlockedSettingAsUser(pkg, userId));
+            mPm.setApplicationHiddenSettingAsUser(pkg, state, userId);
+            System.err.println("Package " + pkg + " new hidden state: "
+                    + mPm.getApplicationHiddenSettingAsUser(pkg, userId));
         } catch (RemoteException e) {
             System.err.println(e.toString());
             System.err.println(PM_NOT_RUNNING_ERR);
@@ -1751,8 +1751,8 @@
         System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
-        System.err.println("       pm block [--user USER_ID] PACKAGE_OR_COMPONENT");
-        System.err.println("       pm unblock [--user USER_ID] PACKAGE_OR_COMPONENT");
+        System.err.println("       pm hide [--user USER_ID] PACKAGE_OR_COMPONENT");
+        System.err.println("       pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm grant PACKAGE PERMISSION");
         System.err.println("       pm revoke PACKAGE PERMISSION");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 990ea85..caadecb 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -94,6 +94,13 @@
      */
     public static final int MODE_ERRORED = 2;
 
+    /**
+     * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller should
+     * use its default security check.  This mode is not normally used; it should only be used
+     * with appop permissions, and callers must explicitly check for it and deal with it.
+     */
+    public static final int MODE_DEFAULT = 3;
+
     // when adding one of these:
     //  - increment _NUM_OP
     //  - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode
@@ -588,7 +595,7 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
-            AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
+            AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6a51371..1cb0fd4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1452,10 +1452,10 @@
     }
 
     @Override
-    public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+    public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
             UserHandle user) {
         try {
-            return mPM.setApplicationBlockedSettingAsUser(packageName, blocked,
+            return mPM.setApplicationHiddenSettingAsUser(packageName, hidden,
                     user.getIdentifier());
         } catch (RemoteException re) {
             // Should never happen!
@@ -1464,9 +1464,9 @@
     }
 
     @Override
-    public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle user) {
+    public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
         try {
-            return mPM.getApplicationBlockedSettingAsUser(packageName, user.getIdentifier());
+            return mPM.getApplicationHiddenSettingAsUser(packageName, user.getIdentifier());
         } catch (RemoteException re) {
             // Should never happen!
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5f2262b..efeded5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2523,20 +2523,20 @@
     }
 
     /**
-     * Called by device or profile owner to block or unblock packages. When a package is blocked it
+     * Called by device or profile owner to hide or unhide packages. When a package is hidden it
      * is unavailable for use, but the data and actual package file remain.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param packageName The name of the package to block or unblock.
-     * @param blocked {@code true} if the package should be blocked, {@code false} if it should be
-     *                 unblocked.
-     * @return boolean Whether the blocked setting of the package was successfully updated.
+     * @param packageName The name of the package to hide or unhide.
+     * @param hidden {@code true} if the package should be hidden, {@code false} if it should be
+     *                 unhidden.
+     * @return boolean Whether the hidden setting of the package was successfully updated.
      */
-    public boolean setApplicationBlocked(ComponentName admin, String packageName,
-            boolean blocked) {
+    public boolean setApplicationHidden(ComponentName admin, String packageName,
+            boolean hidden) {
         if (mService != null) {
             try {
-                return mService.setApplicationBlocked(admin, packageName, blocked);
+                return mService.setApplicationHidden(admin, packageName, hidden);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2545,20 +2545,20 @@
     }
 
     /**
-     * Called by profile or device owner to block or unblock currently installed packages. This
+     * Called by profile or device owner to hide or unhide currently installed packages. This
      * should only be called by a profile or device owner running within a managed profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param intent An intent matching the app(s) to be updated. All apps that resolve for this
      *               intent will be updated in the current profile.
-     * @param blocked {@code true} if the packages should be blocked, {@code false} if they should
-     *                 be unblocked.
+     * @param hidden {@code true} if the packages should be hidden, {@code false} if they should
+     *                 be unhidden.
      * @return int The number of activities that matched the intent and were updated.
      */
-    public int setApplicationsBlocked(ComponentName admin, Intent intent, boolean blocked) {
+    public int setApplicationsHidden(ComponentName admin, Intent intent, boolean hidden) {
         if (mService != null) {
             try {
-                return mService.setApplicationsBlocked(admin, intent, blocked);
+                return mService.setApplicationsHidden(admin, intent, hidden);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2567,16 +2567,16 @@
     }
 
     /**
-     * Called by device or profile owner to determine if a package is blocked.
+     * Called by device or profile owner to determine if a package is hidden.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param packageName The name of the package to retrieve the blocked status of.
-     * @return boolean {@code true} if the package is blocked, {@code false} otherwise.
+     * @param packageName The name of the package to retrieve the hidden status of.
+     * @return boolean {@code true} if the package is hidden, {@code false} otherwise.
      */
-    public boolean isApplicationBlocked(ComponentName admin, String packageName) {
+    public boolean isApplicationHidden(ComponentName admin, String packageName) {
         if (mService != null) {
             try {
-                return mService.isApplicationBlocked(admin, packageName);
+                return mService.isApplicationHidden(admin, packageName);
             } 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 9b1979f..5fc8c5f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,9 +132,9 @@
     void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
     void clearCrossProfileIntentFilters(in ComponentName admin);
 
-    boolean setApplicationBlocked(in ComponentName admin, in String packageName, boolean blocked);
-    int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked);
-    boolean isApplicationBlocked(in ComponentName admin, in String packageName);
+    boolean setApplicationHidden(in ComponentName admin, in String packageName, boolean hidden);
+    int setApplicationsHidden(in ComponentName admin, in Intent intent, boolean hidden);
+    boolean isApplicationHidden(in ComponentName admin, in String packageName);
 
     UserHandle createUser(in ComponentName who, in String name);
     UserHandle createAndInitializeUser(in ComponentName who, in String name, in String profileOwnerName, in ComponentName profileOwnerComponent, in Bundle adminExtras);
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 35ff665..57d2011 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -134,6 +134,7 @@
         mLastTimeSaved = stats.mLastTimeSaved;
 
         final int pkgCount = stats.mPackageStats.size();
+        mPackageStats.ensureCapacity(pkgCount);
         for (int i = 0; i < pkgCount; i++) {
             PackageUsageStats pkgStats = stats.mPackageStats.valueAt(i);
             mPackageStats.append(stats.mPackageStats.keyAt(i), new PackageUsageStats(pkgStats));
diff --git a/core/java/android/content/AbstractRestrictionsProvider.java b/core/java/android/content/AbstractRestrictionsProvider.java
index 3272970..2b40870 100644
--- a/core/java/android/content/AbstractRestrictionsProvider.java
+++ b/core/java/android/content/AbstractRestrictionsProvider.java
@@ -17,8 +17,8 @@
 package android.content;
 
 import android.app.admin.DevicePolicyManager;
-import android.os.Bundle;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 
 /**
  * Abstract implementation of a Restrictions Provider BroadcastReceiver. To implement a
@@ -30,7 +30,7 @@
  * The function of a Restrictions Provider is to transport permission requests from apps on this
  * device to an administrator (most likely on a remote device or computer) and deliver back
  * responses. The response should be sent back to the app via
- * {@link RestrictionsManager#notifyPermissionResponse(String, Bundle)}.
+ * {@link RestrictionsManager#notifyPermissionResponse(String, PersistableBundle)}.
  *
  * @see RestrictionsManager
  */
@@ -59,7 +59,7 @@
      * @see RestrictionsManager#REQUEST_KEY_ID
      */
     public abstract void requestPermission(Context context,
-            String packageName, String requestType, Bundle request);
+            String packageName, String requestType, String requestId, PersistableBundle request);
 
     /**
      * Intercept standard Restrictions Provider broadcasts.  Implementations
@@ -73,8 +73,10 @@
         if (RestrictionsManager.ACTION_REQUEST_PERMISSION.equals(action)) {
             String packageName = intent.getStringExtra(RestrictionsManager.EXTRA_PACKAGE_NAME);
             String requestType = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_TYPE);
-            Bundle request = intent.getBundleExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE);
-            requestPermission(context, packageName, requestType, request);
+            String requestId = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_ID);
+            PersistableBundle request = (PersistableBundle)
+                    intent.getParcelableExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE);
+            requestPermission(context, packageName, requestType, requestId, request);
         }
     }
 }
diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl
index b1c0a3a..495ac2e 100644
--- a/core/java/android/content/IRestrictionsManager.aidl
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -16,7 +16,9 @@
 
 package android.content;
 
+import android.content.Intent;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 
 /**
  * Interface used by the RestrictionsManager
@@ -25,6 +27,8 @@
 interface IRestrictionsManager {
     Bundle getApplicationRestrictions(in String packageName);
     boolean hasRestrictionsProvider();
-    void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData);
-    void notifyPermissionResponse(in String packageName, in Bundle response);
+    void requestPermission(in String packageName, in String requestType, in String requestId,
+            in PersistableBundle requestData);
+    void notifyPermissionResponse(in String packageName, in PersistableBundle response);
+    Intent getLocalApprovalIntent();
 }
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 5ae10cfc..c1226c0 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -23,6 +24,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -51,7 +53,7 @@
  * <p>
  * The RestrictionsManager forwards the dynamic requests to the active
  * Restrictions Provider. The Restrictions Provider can respond back to requests by calling
- * {@link #notifyPermissionResponse(String, Bundle)}, when
+ * {@link #notifyPermissionResponse(String, PersistableBundle)}, when
  * a response is received from the administrator of the device or user.
  * The response is relayed back to the application via a protected broadcast,
  * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
@@ -126,14 +128,15 @@
      * {@link #EXTRA_RESPONSE_BUNDLE}.
      */
     public static final String ACTION_PERMISSION_RESPONSE_RECEIVED =
-            "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
+            "android.content.action.PERMISSION_RESPONSE_RECEIVED";
 
     /**
      * Broadcast intent sent to the Restrictions Provider to handle a permission request from
      * an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME},
-     * {@link #EXTRA_REQUEST_TYPE} and {@link #EXTRA_REQUEST_BUNDLE}. The Restrictions Provider
-     * will handle the request and respond back to the RestrictionsManager, when a response is
-     * available, by calling {@link #notifyPermissionResponse}.
+     * {@link #EXTRA_REQUEST_TYPE}, {@link #EXTRA_REQUEST_ID} and {@link #EXTRA_REQUEST_BUNDLE}.
+     * The Restrictions Provider will handle the request and respond back to the
+     * RestrictionsManager, when a response is available, by calling
+     * {@link #notifyPermissionResponse}.
      * <p>
      * The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN}
      * permission to ensure that only the system can send the broadcast.
@@ -142,17 +145,45 @@
             "android.content.action.REQUEST_PERMISSION";
 
     /**
+     * Activity intent that is optionally implemented by the Restrictions Provider package
+     * to challenge for an administrator PIN or password locally on the device. Apps will
+     * call this intent using {@link Activity#startActivityForResult}. On a successful
+     * response, {@link Activity#onActivityResult} will return a resultCode of
+     * {@link Activity#RESULT_OK}.
+     * <p>
+     * The intent must contain {@link #EXTRA_REQUEST_BUNDLE} as an extra and the bundle must
+     * contain at least {@link #REQUEST_KEY_MESSAGE} for the activity to display.
+     * <p>
+     * @see #getLocalApprovalIntent()
+     */
+    public static final String ACTION_REQUEST_LOCAL_APPROVAL =
+            "android.content.action.REQUEST_LOCAL_APPROVAL";
+
+    /**
      * The package name of the application making the request.
+     * <p>
+     * Type: String
      */
     public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
 
     /**
      * The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     * <p>
+     * Type: String
      */
     public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
 
     /**
+     * The request ID passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     * <p>
+     * Type: String
+     */
+    public static final String EXTRA_REQUEST_ID = "android.content.extra.REQUEST_ID";
+
+    /**
      * The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     * <p>
+     * Type: {@link PersistableBundle}
      */
     public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
 
@@ -163,14 +194,15 @@
      * <li>{@link #REQUEST_KEY_ID}: The request ID.</li>
      * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li>
      * </ul>
+     * <p>
+     * Type: {@link PersistableBundle}
      */
     public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
 
     /**
      * Request type for a simple question, with a possible title and icon.
      * <p>
-     * Required keys are
-     * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}.
+     * Required keys are: {@link #REQUEST_KEY_MESSAGE}
      * <p>
      * Optional keys are
      * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
@@ -179,22 +211,6 @@
     public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
 
     /**
-     * Request type for a local password challenge. This is a way for an app to ask
-     * the administrator to override an operation that is restricted on the device, such
-     * as configuring Wi-Fi access points. It is most useful for situations where there
-     * is no network connectivity for a remote administrator's response. The normal user of the
-     * device is not expected to know the password. The challenge is meant for the administrator.
-     * <p>
-     * Required keys are
-     * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}.
-     * <p>
-     * Optional keys are
-     * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
-     * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
-     */
-    public static final String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
-
-    /**
      * Key for request ID contained in the request bundle.
      * <p>
      * App-generated request ID to identify the specific request when receiving
@@ -240,9 +256,10 @@
      * Key for request icon contained in the request bundle.
      * <p>
      * Optional, shown alongside the request message presented to the administrator
-     * who approves the request.
+     * who approves the request. The content must be a compressed image such as a
+     * PNG or JPEG, as a byte array.
      * <p>
-     * Type: Bitmap
+     * Type: byte[]
      */
     public static final String REQUEST_KEY_ICON = "android.request.icon";
 
@@ -403,7 +420,7 @@
 
     /**
      * Called by an application to check if there is an active Restrictions Provider. If
-     * there isn't, {@link #requestPermission(String, Bundle)} is not available.
+     * there isn't, {@link #requestPermission(String, String, PersistableBundle)} is not available.
      *
      * @return whether there is an active Restrictions Provider.
      */
@@ -428,40 +445,54 @@
      * Restrictions Provider might understand. For custom types, the type name should be
      * namespaced to avoid collisions with predefined types and types specified by
      * other Restrictions Providers.
-     * @param request A Bundle containing the data corresponding to the specified request
+     * @param requestId A unique id generated by the app that contains sufficient information
+     * to identify the parameters of the request when it receives the id in the response.
+     * @param request A PersistableBundle containing the data corresponding to the specified request
      * type. The keys for the data in the bundle depend on the request type.
      *
      * @throws IllegalArgumentException if any of the required parameters are missing.
      */
-    public void requestPermission(String requestType, Bundle request) {
+    public void requestPermission(String requestType, String requestId, PersistableBundle request) {
         if (requestType == null) {
             throw new NullPointerException("requestType cannot be null");
         }
+        if (requestId == null) {
+            throw new NullPointerException("requestId cannot be null");
+        }
         if (request == null) {
             throw new NullPointerException("request cannot be null");
         }
-        if (!request.containsKey(REQUEST_KEY_ID)) {
-            throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
-        }
         try {
             if (mService != null) {
-                mService.requestPermission(mContext.getPackageName(), requestType, request);
+                mService.requestPermission(mContext.getPackageName(), requestType, requestId,
+                        request);
             }
         } catch (RemoteException re) {
             Log.w(TAG, "Couldn't reach service");
         }
     }
 
+    public Intent getLocalApprovalIntent() {
+        try {
+            if (mService != null) {
+                return mService.getLocalApprovalIntent();
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Couldn't reach service");
+        }
+        return null;
+    }
+
     /**
      * Called by the Restrictions Provider to deliver a response to an application.
      *
      * @param packageName the application to deliver the response to. Cannot be null.
-     * @param response the Bundle containing the response status, request ID and other information.
+     * @param response the bundle containing the response status, request ID and other information.
      *                 Cannot be null.
      *
      * @throws IllegalArgumentException if any of the required parameters are missing.
      */
-    public void notifyPermissionResponse(String packageName, Bundle response) {
+    public void notifyPermissionResponse(String packageName, PersistableBundle response) {
         if (packageName == null) {
             throw new NullPointerException("packageName cannot be null");
         }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index bcf1e87..dbf49c5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -112,7 +112,7 @@
      * Constant corresponding to <code>doNotPersist</code> in
      * the {@link android.R.attr#persistableMode} attribute.
      */
-    public static final int DO_NOT_PERSIST = 1;
+    public static final int PERSIST_NEVER = 1;
     /**
      * Constant corresponding to <code>persistAcrossReboots</code> in
      * the {@link android.R.attr#persistableMode} attribute.
@@ -667,7 +667,7 @@
     private String persistableModeToString() {
         switch(persistableMode) {
             case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY";
-            case DO_NOT_PERSIST: return "DO_NOT_PERSIST";
+            case PERSIST_NEVER: return "PERSIST_NEVER";
             case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS";
             default: return "UNKNOWN=" + persistableMode;
         }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 95bd480..482ad6e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -334,11 +334,11 @@
     public static final int FLAG_FULL_BACKUP_ONLY = 1<<26;
 
     /**
-     * Value for {@link #flags}: true if the application is blocked via restrictions and for
+     * Value for {@link #flags}: true if the application is hidden via restrictions and for
      * most purposes is considered as not installed.
      * {@hide}
      */
-    public static final int FLAG_BLOCKED = 1<<27;
+    public static final int FLAG_HIDDEN = 1<<27;
 
     /**
      * Value for {@link #flags}: set to <code>true</code> if the application
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eb46cf0..4b339a1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -110,6 +110,8 @@
 
     int getFlagsForUid(int uid);
 
+    String[] getAppOpPermissionPackages(String permissionName);
+
     ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
 
     boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
@@ -428,8 +430,8 @@
     /** Reflects current DeviceStorageMonitorService state */
     boolean isStorageLow();
 
-    boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked, int userId);
-    boolean getApplicationBlockedSettingAsUser(String packageName, int userId);
+    boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId);
+    boolean getApplicationHiddenSettingAsUser(String packageName, int userId);
 
     IPackageInstaller getPackageInstaller();
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b4c0219..8b6ae41 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3642,20 +3642,21 @@
     public abstract int getApplicationEnabledSetting(String packageName);
 
     /**
-     * Puts the package in a blocked state, which is almost like an uninstalled state,
+     * Puts the package in a hidden state, which is almost like an uninstalled state,
      * making the package unavailable, but it doesn't remove the data or the actual
-     * package file.
+     * package file. Application can be unhidden by either resetting the hidden state
+     * or by installing it, such as with {@link #installExistingPackage(String)}
      * @hide
      */
-    public abstract boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+    public abstract boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
             UserHandle userHandle);
 
     /**
-     * Returns the blocked state of a package.
-     * @see #setApplicationBlockedSettingAsUser(String, boolean, UserHandle)
+     * Returns the hidden state of a package.
+     * @see #setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
      * @hide
      */
-    public abstract boolean getApplicationBlockedSettingAsUser(String packageName,
+    public abstract boolean getApplicationHiddenSettingAsUser(String packageName,
             UserHandle userHandle);
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 833dc21..9866200 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -382,23 +382,23 @@
     }
 
     /**
-     * Returns true if the package is installed and not blocked, or if the caller
-     * explicitly wanted all uninstalled and blocked packages as well.
+     * Returns true if the package is installed and not hidden, or if the caller
+     * explicitly wanted all uninstalled and hidden packages as well.
      */
-    private static boolean checkUseInstalledOrBlocked(int flags, PackageUserState state) {
-        return (state.installed && !state.blocked)
+    private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state) {
+        return (state.installed && !state.hidden)
                 || (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
     }
 
     public static boolean isAvailable(PackageUserState state) {
-        return checkUseInstalledOrBlocked(0, state);
+        return checkUseInstalledOrHidden(0, state);
     }
 
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
             HashSet<String> grantedPermissions, PackageUserState state, int userId) {
 
-        if (!checkUseInstalledOrBlocked(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state)) {
             return null;
         }
         PackageInfo pi = new PackageInfo();
@@ -4533,7 +4533,7 @@
                 return true;
             }
         }
-        if (!state.installed || state.blocked) {
+        if (!state.installed || state.hidden) {
             return true;
         }
         if (state.stopped) {
@@ -4566,10 +4566,10 @@
         } else {
             ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
         }
-        if (state.blocked) {
-            ai.flags |= ApplicationInfo.FLAG_BLOCKED;
+        if (state.hidden) {
+            ai.flags |= ApplicationInfo.FLAG_HIDDEN;
         } else {
-            ai.flags &= ~ApplicationInfo.FLAG_BLOCKED;
+            ai.flags &= ~ApplicationInfo.FLAG_HIDDEN;
         }
         if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
             ai.enabled = true;
@@ -4585,7 +4585,7 @@
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
-        if (!checkUseInstalledOrBlocked(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state)) {
             return null;
         }
         if (!copyNeeded(flags, p, state, null, userId)
@@ -4673,7 +4673,7 @@
     public static final ActivityInfo generateActivityInfo(Activity a, int flags,
             PackageUserState state, int userId) {
         if (a == null) return null;
-        if (!checkUseInstalledOrBlocked(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state)) {
             return null;
         }
         if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
@@ -4714,7 +4714,7 @@
     public static final ServiceInfo generateServiceInfo(Service s, int flags,
             PackageUserState state, int userId) {
         if (s == null) return null;
-        if (!checkUseInstalledOrBlocked(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state)) {
             return null;
         }
         if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
@@ -4763,7 +4763,7 @@
     public static final ProviderInfo generateProviderInfo(Provider p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
-        if (!checkUseInstalledOrBlocked(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state)) {
             return null;
         }
         if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index f0d4cb1..4dcad6f 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -28,7 +28,7 @@
     public boolean stopped;
     public boolean notLaunched;
     public boolean installed;
-    public boolean blocked; // Is the app restricted by owner / admin
+    public boolean hidden; // Is the app restricted by owner / admin
     public int enabled;
     public boolean blockUninstall;
 
@@ -39,7 +39,7 @@
 
     public PackageUserState() {
         installed = true;
-        blocked = false;
+        hidden = false;
         enabled = COMPONENT_ENABLED_STATE_DEFAULT;
     }
 
@@ -48,7 +48,7 @@
         stopped = o.stopped;
         notLaunched = o.notLaunched;
         enabled = o.enabled;
-        blocked = o.blocked;
+        hidden = o.hidden;
         lastDisableAppCaller = o.lastDisableAppCaller;
         disabledComponents = o.disabledComponents != null
                 ? new HashSet<String>(o.disabledComponents) : null;
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5a63e5f..af574db 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -69,6 +69,13 @@
     public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
 
     /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>development</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_APPOP = 0x40;
+
+    /**
      * Mask for {@link #protectionLevel}: the basic protection type.
      */
     public static final int PROTECTION_MASK_BASE = 0xf;
@@ -153,6 +160,9 @@
         if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
             protLevel += "|development";
         }
+        if ((level&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+            protLevel += "|appop";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f2426e5..1b9a0c5 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -471,22 +471,30 @@
         private VirtualDisplayCallbacksDelegate mDelegate;
 
         public VirtualDisplayCallbacks(VirtualDisplay.Callbacks callbacks, Handler handler) {
-            mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler);
+            if (callbacks != null) {
+                mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler);
+            }
         }
 
         @Override // Binder call
         public void onDisplayPaused() {
-            mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED);
+            if (mDelegate != null) {
+                mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED);
+            }
         }
 
         @Override // Binder call
         public void onDisplayResumed() {
-            mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED);
+            if (mDelegate != null) {
+                mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED);
+            }
         }
 
         @Override // Binder call
         public void onDisplayStopped() {
-            mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED);
+            if (mDelegate != null) {
+                mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED);
+            }
         }
     }
 
@@ -505,9 +513,6 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (mCallbacks == null) {
-                return;
-            }
             switch (msg.what) {
                 case MSG_DISPLAY_PAUSED:
                     mCallbacks.onDisplayPaused();
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index e58c54d..99af2e7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -19,6 +19,7 @@
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.view.Display;
 import android.view.DisplayInfo;
 
 /**
@@ -132,13 +133,19 @@
      * have to micro-manage screen off animations, auto-brightness and other effects.
      */
     public static final class DisplayPowerRequest {
-        public static final int SCREEN_STATE_OFF = 0;
-        public static final int SCREEN_STATE_DOZE = 1;
-        public static final int SCREEN_STATE_DIM = 2;
-        public static final int SCREEN_STATE_BRIGHT = 3;
+        // Policy: Turn screen off as if the user pressed the power button
+        // including playing a screen off animation if applicable.
+        public static final int POLICY_OFF = 0;
+        // Policy: Enable dozing and always-on display functionality.
+        public static final int POLICY_DOZE = 1;
+        // Policy: Make the screen dim when the user activity timeout is
+        // about to expire.
+        public static final int POLICY_DIM = 2;
+        // Policy: Make the screen bright as usual.
+        public static final int POLICY_BRIGHT = 3;
 
-        // The requested minimum screen power state: off, doze, dim or bright.
-        public int screenState;
+        // The basic overall policy to apply: off, doze, dim or bright.
+        public int policy;
 
         // If true, the proximity sensor overrides the screen state when an object is
         // nearby, turning it off temporarily until the object is moved away.
@@ -169,44 +176,39 @@
         // visible to the user.
         public boolean blockScreenOn;
 
+        // Overrides the policy for adjusting screen brightness and state while dozing.
+        public int dozeScreenBrightness;
+        public int dozeScreenState;
+
         public DisplayPowerRequest() {
-            screenState = SCREEN_STATE_BRIGHT;
+            policy = POLICY_BRIGHT;
             useProximitySensor = false;
             screenBrightness = PowerManager.BRIGHTNESS_ON;
             screenAutoBrightnessAdjustment = 0.0f;
             useAutoBrightness = false;
             blockScreenOn = false;
+            dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+            dozeScreenState = Display.STATE_UNKNOWN;
         }
 
         public DisplayPowerRequest(DisplayPowerRequest other) {
             copyFrom(other);
         }
 
-        // Returns true if we want the screen on in any mode, including doze.
-        public boolean wantScreenOnAny() {
-            return screenState != SCREEN_STATE_OFF;
-        }
-
-        // Returns true if we want the screen on in a normal mode, excluding doze.
-        // This is usually what we want to tell the rest of the system.  For compatibility
-        // reasons, we pretend the screen is off when dozing.
-        public boolean wantScreenOnNormal() {
-            return screenState == SCREEN_STATE_DIM || screenState == SCREEN_STATE_BRIGHT;
-        }
-
-        public boolean wantLightSensorEnabled() {
-            // Specifically, we don't want the light sensor while dozing.
-            return useAutoBrightness && wantScreenOnNormal();
+        public boolean isBrightOrDim() {
+            return policy == POLICY_BRIGHT || policy == POLICY_DIM;
         }
 
         public void copyFrom(DisplayPowerRequest other) {
-            screenState = other.screenState;
+            policy = other.policy;
             useProximitySensor = other.useProximitySensor;
             screenBrightness = other.screenBrightness;
             screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
             useAutoBrightness = other.useAutoBrightness;
             blockScreenOn = other.blockScreenOn;
             lowPowerMode = other.lowPowerMode;
+            dozeScreenBrightness = other.dozeScreenBrightness;
+            dozeScreenState = other.dozeScreenState;
         }
 
         @Override
@@ -217,13 +219,15 @@
 
         public boolean equals(DisplayPowerRequest other) {
             return other != null
-                    && screenState == other.screenState
+                    && policy == other.policy
                     && useProximitySensor == other.useProximitySensor
                     && screenBrightness == other.screenBrightness
                     && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
                     && useAutoBrightness == other.useAutoBrightness
                     && blockScreenOn == other.blockScreenOn
-                    && lowPowerMode == other.lowPowerMode;
+                    && lowPowerMode == other.lowPowerMode
+                    && dozeScreenBrightness == other.dozeScreenBrightness
+                    && dozeScreenState == other.dozeScreenState;
         }
 
         @Override
@@ -233,13 +237,30 @@
 
         @Override
         public String toString() {
-            return "screenState=" + screenState
+            return "policy=" + policyToString(policy)
                     + ", useProximitySensor=" + useProximitySensor
                     + ", screenBrightness=" + screenBrightness
                     + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
                     + ", useAutoBrightness=" + useAutoBrightness
                     + ", blockScreenOn=" + blockScreenOn
-                    + ", lowPowerMode=" + lowPowerMode;
+                    + ", lowPowerMode=" + lowPowerMode
+                    + ", dozeScreenBrightness=" + dozeScreenBrightness
+                    + ", dozeScreenState=" + Display.stateToString(dozeScreenState);
+        }
+
+        public static String policyToString(int policy) {
+            switch (policy) {
+                case POLICY_OFF:
+                    return "OFF";
+                case POLICY_DOZE:
+                    return "DOZE";
+                case POLICY_DIM:
+                    return "DIM";
+                case POLICY_BRIGHT:
+                    return "BRIGHT";
+                default:
+                    return Integer.toString(policy);
+            }
         }
     }
 
diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
index ae0bda1..acf92f1 100644
--- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
@@ -57,7 +57,13 @@
     // Value indicating the device is not an active source.
     public static final int DEVICE_INACTIVE = -1;
 
-    // Logical address, phsical address, device type, vendor id and display name
+    /**
+     * Logical address used to indicate the source comes from internal device.
+     * The logical address of TV(0) is used.
+     */
+    public static final int ADDR_INTERNAL = 0;
+
+    // Logical address, physical address, device type, vendor id and display name
     // are immutable value.
     private final int mLogicalAddress;
     private final int mPhysicalAddress;
diff --git a/core/java/android/os/PersistableBundle.aidl b/core/java/android/os/PersistableBundle.aidl
new file mode 100644
index 0000000..5b05873
--- /dev/null
+++ b/core/java/android/os/PersistableBundle.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable PersistableBundle;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 92e80a5..dda6d27 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -263,6 +263,12 @@
      */
     public static final int BRIGHTNESS_OFF = 0;
 
+    /**
+     * Brightness value for default policy handling by the system.
+     * @hide
+     */
+    public static final int BRIGHTNESS_DEFAULT = -1;
+
     // Note: Be sure to update android.os.BatteryStats and PowerManager.h
     // if adding or modifying user activity event constants.
 
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 08a15eb..14f4a83 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.view.Display;
+
 /**
  * Power manager local system service interface.
  *
@@ -53,6 +55,17 @@
      */
     public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis);
 
+    /**
+     * Used by the dream manager to override certain properties while dozing.
+     *
+     * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN}
+     * to disable the override.
+     * @param screenBrightness The overridden screen brightness, or
+     * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
+     */
+    public abstract void setDozeOverrideFromDreamManager(
+            int screenState, int screenBrightness);
+
     public abstract boolean getLowPowerModeEnabled();
 
     public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 1a30c67..46ff58c 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -17,12 +17,16 @@
 
 package android.provider;
 
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.ContactsContract.CommonDataKinds.Callable;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.DataUsageFeedback;
@@ -32,6 +36,8 @@
 import com.android.internal.telephony.CallerInfo;
 import com.android.internal.telephony.PhoneConstants;
 
+import java.util.List;
+
 /**
  * The CallLog provider contains information about placed and received calls.
  */
@@ -348,12 +354,44 @@
          * @param duration call duration in seconds
          * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
          *                  the call.
-         *
+         * @result The URI of the call log entry belonging to the user that made or received this
+         *        call.
          * {@hide}
          */
         public static Uri addCall(CallerInfo ci, Context context, String number,
                 int presentation, int callType, int features, PhoneAccountHandle accountHandle,
                 long start, int duration, Long dataUsage) {
+            return addCall(ci, context, number, presentation, callType, features, accountHandle,
+                    start, duration, dataUsage, false);
+        }
+
+        /**
+         * Adds a call to the call log.
+         *
+         * @param ci the CallerInfo object to get the target contact from.  Can be null
+         * if the contact is unknown.
+         * @param context the context used to get the ContentResolver
+         * @param number the phone number to be added to the calls db
+         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
+         *        is set by the network and denotes the number presenting rules for
+         *        "allowed", "payphone", "restricted" or "unknown"
+         * @param callType enumerated values for "incoming", "outgoing", or "missed"
+         * @param features features of the call (e.g. Video).
+         * @param accountHandle The accountHandle object identifying the provider of the call
+         * @param start time stamp for the call in milliseconds
+         * @param duration call duration in seconds
+         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
+         *                  the call.
+         * @param addForAllUsers If true, the call is added to the call log of all currently
+         *        running users. The caller must have the MANAGE_USERS permission if this is true.
+         *
+         * @result The URI of the call log entry belonging to the user that made or received this
+         *        call.
+         * {@hide}
+         */
+        public static Uri addCall(CallerInfo ci, Context context, String number,
+                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
+                long start, int duration, Long dataUsage, boolean addForAllUsers) {
             final ContentResolver resolver = context.getContentResolver();
             int numberPresentation = PRESENTATION_ALLOWED;
 
@@ -452,9 +490,32 @@
                 }
             }
 
-            Uri result = resolver.insert(CONTENT_URI, values);
+            Uri result = null;
 
-            removeExpiredEntries(context);
+            if (addForAllUsers) {
+                // Insert the entry for all currently running users, in order to trigger any
+                // ContentObservers currently set on the call log.
+                final UserManager userManager = (UserManager) context.getSystemService(
+                        Context.USER_SERVICE);
+                List<UserInfo> users = userManager.getUsers(true);
+                final int currentUserId = userManager.getUserHandle();
+                final int count = users.size();
+                for (int i = 0; i < count; i++) {
+                    final UserInfo user = users.get(i);
+                    final UserHandle userHandle = user.getUserHandle();
+                    if (userManager.isUserRunning(userHandle) &&
+                            !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
+                                    userHandle)) {
+                        Uri uri = addEntryAndRemoveExpiredEntries(context,
+                                ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values);
+                        if (user.id == currentUserId) {
+                            result = uri;
+                        }
+                    }
+                }
+            } else {
+                result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values);
+            }
 
             return result;
         }
@@ -484,11 +545,14 @@
             }
         }
 
-        private static void removeExpiredEntries(Context context) {
+        private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
+                ContentValues values) {
             final ContentResolver resolver = context.getContentResolver();
-            resolver.delete(CONTENT_URI, "_id IN " +
+            Uri result = resolver.insert(uri, values);
+            resolver.delete(uri, "_id IN " +
                     "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
                     + " LIMIT -1 OFFSET 500)", null);
+            return result;
         }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bbaf1c1..34d7c80 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3937,30 +3937,12 @@
                 "accessibility_captioning_font_scale";
 
         /**
-         * Setting that specifies whether the quick setting tile for display
-         * color inversion is enabled.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED =
-                "accessibility_display_inversion_quick_setting_enabled";
-
-        /**
          * Setting that specifies whether display color inversion is enabled.
          */
         public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
                 "accessibility_display_inversion_enabled";
 
         /**
-         * Setting that specifies whether the quick setting tile for display
-         * color space adjustment is enabled.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED =
-                "accessibility_display_daltonizer_quick_setting_enabled";
-
-        /**
          * Setting that specifies whether display color space adjustment is
          * enabled.
          *
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5fa542a..5cf8aa6 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -27,10 +27,13 @@
 import android.graphics.drawable.ColorDrawable;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Slog;
 import android.view.ActionMode;
+import android.view.Display;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -42,6 +45,7 @@
 import android.view.WindowManagerGlobal;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
+import android.util.MathUtils;
 
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.util.DumpUtils;
@@ -133,8 +137,11 @@
  *     android:exported="true"
  *     android:icon="@drawable/my_icon"
  *     android:label="@string/my_dream_label"
- *     android:permission="android.permission.BIND_DREAM_SERVICE" >
- *  ...
+ *     android:permission="android.permission.BIND_DREAM_SERVICE">
+ *   &lt;intent-filter>
+ *     &lt;action android:name=”android.service.dreams.DreamService” />
+ *     &lt;category android:name=”android.intent.category.DEFAULT” />
+ *   &lt;/intent-filter>
  * &lt;/service>
  * </pre>
  */
@@ -177,6 +184,8 @@
     private boolean mDozing;
     private boolean mWindowless;
     private DozeHardware mDozeHardware;
+    private int mDozeScreenState = Display.STATE_UNKNOWN;
+    private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
 
     private boolean mDebug = false;
 
@@ -560,7 +569,7 @@
      *
      * @return True if this dream can doze.
      * @see #startDozing
-     * @hide experimental
+     * @hide For use by system UI components only.
      */
     public boolean canDoze() {
         return mCanDoze;
@@ -593,13 +602,19 @@
      * </p>
      *
      * @see #stopDozing
-     * @hide experimental
+     * @hide For use by system UI components only.
      */
     public void startDozing() {
         if (mCanDoze && !mDozing) {
             mDozing = true;
+            updateDoze();
+        }
+    }
+
+    private void updateDoze() {
+        if (mDozing) {
             try {
-                mSandman.startDozing(mWindowToken);
+                mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -615,7 +630,7 @@
      * </p>
      *
      * @see #startDozing
-     * @hide experimental
+     * @hide For use by system UI components only.
      */
     public void stopDozing() {
         if (mDozing) {
@@ -636,7 +651,7 @@
      * @return True if the dream is dozing.
      *
      * @see #setDozing(boolean)
-     * @hide experimental
+     * @hide For use by system UI components only.
      */
     public boolean isDozing() {
         return mDozing;
@@ -649,7 +664,7 @@
      * @return An instance of {@link DozeHardware} or null if this device does not offer
      * hardware support for dozing.
      *
-     * @hide experimental
+     * @hide For use by system UI components only.
      */
     public DozeHardware getDozeHardware() {
         if (mCanDoze && mDozeHardware == null && mWindowToken != null) {
@@ -666,11 +681,116 @@
     }
 
     /**
+     * Gets the screen state to use while dozing.
+     *
+     * @return The screen state to use while dozing, such as {@link Display#STATE_ON},
+     * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND},
+     * or {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} for the default
+     * behavior.
+     *
+     * @see #setDozeScreenState
+     * @hide For use by system UI components only.
+     */
+    public int getDozeScreenState() {
+        return mDozeScreenState;
+    }
+
+    /**
+     * Sets the screen state to use while dozing.
+     * <p>
+     * The value of this property determines the power state of the primary display
+     * once {@link #startDozing} has been called.  The default value is
+     * {@link Display#STATE_UNKNOWN} which lets the system decide.
+     * The dream may set a different state before starting to doze and may
+     * perform transitions between states while dozing to conserve power and
+     * achieve various effects.
+     * </p><p>
+     * It is recommended that the state be set to {@link Display#STATE_DOZE_SUSPEND}
+     * once the dream has completely finished drawing and before it releases its wakelock
+     * to allow the display hardware to be fully suspended.  While suspended, the
+     * display will preserve its on-screen contents or hand off control to dedicated
+     * doze hardware if the devices supports it.  If the doze suspend state is
+     * used, the dream must make sure to set the mode back
+     * to {@link Display#STATE_DOZE} or {@link Display#STATE_ON} before drawing again
+     * since the display updates may be ignored and not seen by the user otherwise.
+     * </p><p>
+     * The set of available display power states and their behavior while dozing is
+     * hardware dependent and may vary across devices.  The dream may therefore
+     * need to be modified or configured to correctly support the hardware.
+     * </p>
+     *
+     * @param state The screen state to use while dozing, such as {@link Display#STATE_ON},
+     * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND},
+     * or {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} for the default
+     * behavior.
+     *
+     * @hide For use by system UI components only.
+     */
+    public void setDozeScreenState(int state) {
+        if (mDozeScreenState != state) {
+            mDozeScreenState = state;
+            updateDoze();
+        }
+    }
+
+    /**
+     * Gets the screen brightness to use while dozing.
+     *
+     * @return The screen brightness while dozing as a value between
+     * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255),
+     * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply
+     * its default policy based on the screen state.
+     *
+     * @see #setDozeScreenBrightness
+     * @hide For use by system UI components only.
+     */
+    public int getDozeScreenBrightness() {
+        return mDozeScreenBrightness;
+    }
+
+    /**
+     * Sets the screen brightness to use while dozing.
+     * <p>
+     * The value of this property determines the power state of the primary display
+     * once {@link #startDozing} has been called.  The default value is
+     * {@link PowerManager#BRIGHTNESS_DEFAULT} which lets the system decide.
+     * The dream may set a different brightness before starting to doze and may adjust
+     * the brightness while dozing to conserve power and achieve various effects.
+     * </p><p>
+     * Note that dream may specify any brightness in the full 0-255 range, including
+     * values that are less than the minimum value for manual screen brightness
+     * adjustments by the user.  In particular, the value may be set to 0 which may
+     * turn off the backlight entirely while still leaving the screen on although
+     * this behavior is device dependent and not guaranteed.
+     * </p><p>
+     * The available range of display brightness values and their behavior while dozing is
+     * hardware dependent and may vary across devices.  The dream may therefore
+     * need to be modified or configured to correctly support the hardware.
+     * </p>
+     *
+     * @param brightness The screen brightness while dozing as a value between
+     * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255),
+     * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply
+     * its default policy based on the screen state.
+     *
+     * @hide For use by system UI components only.
+     */
+    public void setDozeScreenBrightness(int brightness) {
+        if (brightness != PowerManager.BRIGHTNESS_DEFAULT) {
+            brightness = clampAbsoluteBrightness(brightness);
+        }
+        if (mDozeScreenBrightness != brightness) {
+            mDozeScreenBrightness = brightness;
+            updateDoze();
+        }
+    }
+
+    /**
      * Called when this Dream is constructed.
      */
     @Override
     public void onCreate() {
-        if (mDebug) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId());
+        if (mDebug) Slog.v(TAG, "onCreate()");
         super.onCreate();
     }
 
@@ -844,8 +964,6 @@
             return;
         }
 
-        if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId());
-
         mWindowToken = windowToken;
         mCanDoze = canDoze;
         if (mWindowless && !mCanDoze) {
@@ -963,7 +1081,17 @@
         if (isScreenBright()) pw.print(" bright");
         if (isWindowless()) pw.print(" windowless");
         if (isDozing()) pw.print(" dozing");
+        else if (canDoze()) pw.print(" candoze");
         pw.println();
+        if (canDoze()) {
+            pw.println("  doze hardware: " + mDozeHardware);
+            pw.println("  doze screen state: " + Display.stateToString(mDozeScreenState));
+            pw.println("  doze screen brightness: " + mDozeScreenBrightness);
+        }
+    }
+
+    private static int clampAbsoluteBrightness(int value) {
+        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
     }
 
     private final class DreamServiceWrapper extends IDreamService.Stub {
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 9608a4d..648426c 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -32,7 +32,7 @@
     void testDream(in ComponentName componentName);
     boolean isDreaming();
     void finishSelf(in IBinder token, boolean immediate);
-    void startDozing(in IBinder token);
+    void startDozing(in IBinder token, int screenState, int screenBrightness);
     void stopDozing(in IBinder token);
     IDozeHardware getDozeHardware(in IBinder token);
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index b17fa4a..154d227 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -831,4 +831,13 @@
                 return Integer.toString(state);
         }
     }
+
+    /**
+     * Returns true if display updates may be suspended while in the specified
+     * display power state.
+     * @hide
+     */
+    public static boolean isSuspendedState(int state) {
+        return state == STATE_OFF || state == STATE_DOZE_SUSPEND;
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e138345..0079cc9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5048,8 +5048,17 @@
      *         View, false otherwise.
      *
      * @see ViewGroup#FOCUS_BLOCK_DESCENDANTS
+     * @see ViewGroup#getTouchscreenBlocksFocus()
      */
     public boolean hasFocusable() {
+        if (!isFocusableInTouchMode()) {
+            for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
+                final ViewGroup g = (ViewGroup) p;
+                if (g.shouldBlockFocusForTouchscreen()) {
+                    return false;
+                }
+            }
+        }
         return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
     }
 
@@ -7439,11 +7448,12 @@
      * @return Whether any ancestor of this view blocks descendant focus.
      */
     private boolean hasAncestorThatBlocksDescendantFocus() {
+        final boolean focusableInTouchMode = isFocusableInTouchMode();
         ViewParent ancestor = mParent;
         while (ancestor instanceof ViewGroup) {
             final ViewGroup vgAncestor = (ViewGroup) ancestor;
             if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS
-                    || vgAncestor.shouldBlockFocusForTouchscreen()) {
+                    || (!focusableInTouchMode && vgAncestor.shouldBlockFocusForTouchscreen())) {
                 return true;
             } else {
                 ancestor = vgAncestor.getParent();
@@ -15546,7 +15556,8 @@
     }
 
     /**
-     * This function is called whenever the drawable hotspot changes.
+     * This function is called whenever the view hotspot changes and needs to
+     * be propagated to drawables managed by the view.
      * <p>
      * Be sure to call through to the superclass when overriding this function.
      *
@@ -15888,30 +15899,6 @@
     }
 
     /**
-     * Applies a tint to the background drawable.
-     * <p>
-     * Subsequent calls to {@link #setBackground(Drawable)} will automatically
-     * mutate the drawable and apply the specified tint and tint mode using
-     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#View_backgroundTint
-     * @attr ref android.R.styleable#View_backgroundTintMode
-     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
-     */
-    private void setBackgroundTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mBackgroundTint = tint;
-        mBackgroundTintMode = tintMode;
-        mHasBackgroundTint = true;
-
-        applyBackgroundTint();
-    }
-
-    /**
      * Applies a tint to the background drawable. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
@@ -15922,16 +15909,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#View_backgroundTint
-     * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #getBackgroundTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setBackgroundTint(@Nullable ColorStateList tint) {
-        setBackgroundTint(tint, mBackgroundTintMode);
+        mBackgroundTint = tint;
+        mHasBackgroundTint = true;
+
+        applyBackgroundTint();
     }
 
     /**
      * @return the tint applied to the background drawable
      * @attr ref android.R.styleable#View_backgroundTint
-     * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #setBackgroundTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getBackgroundTint() {
@@ -15946,16 +15937,19 @@
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#View_backgroundTintMode
-     * @see #setBackgroundTint(ColorStateList)
+     * @see #getBackgroundTintMode()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setBackgroundTint(mBackgroundTint, tintMode);
+        mBackgroundTintMode = tintMode;
+
+        applyBackgroundTint();
     }
 
     /**
      * @return the blending mode used to apply the tint to the background drawable
      * @attr ref android.R.styleable#View_backgroundTintMode
-     * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #setBackgroundTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getBackgroundTintMode() {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 04c8b0b..1028a0c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -669,7 +669,7 @@
                 // shortcut: don't report a new focusable view if we block our descendants from
                 // getting focus
                 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
-                && !shouldBlockFocusForTouchscreen()
+                && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
                 // shortcut: don't report a new focusable view if we already are focused
                 // (and we don't prefer our descendants)
                 //
@@ -865,6 +865,17 @@
         return mFocused;
     }
 
+    View getDeepestFocusedChild() {
+        View v = this;
+        while (v != null) {
+            if (v.isFocused()) {
+                return v;
+            }
+            v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
+        }
+        return null;
+    }
+
     /**
      * Returns true if this view has or contains focus
      *
@@ -911,8 +922,7 @@
         }
 
         final int descendantFocusability = getDescendantFocusability();
-        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS &&
-                !shouldBlockFocusForTouchscreen()) {
+        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
             final int count = mChildrenCount;
             final View[] children = mChildren;
 
@@ -936,8 +946,11 @@
 
         final int descendantFocusability = getDescendantFocusability();
 
-        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS &&
-                !shouldBlockFocusForTouchscreen()) {
+        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
+            if (shouldBlockFocusForTouchscreen()) {
+                focusableMode |= FOCUSABLES_TOUCH_MODE;
+            }
+
             final int count = mChildrenCount;
             final View[] children = mChildren;
 
@@ -955,7 +968,8 @@
         // among the focusable children would be more interesting.
         if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS
                 // No focusable descendants
-                || (focusableCount == views.size())) && !shouldBlockFocusForTouchscreen()) {
+                || (focusableCount == views.size())) &&
+                (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) {
             super.addFocusables(views, direction, focusableMode);
         }
     }
@@ -971,9 +985,12 @@
         if (touchscreenBlocksFocus) {
             mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
             if (hasFocus()) {
-                final View newFocus = focusSearch(FOCUS_FORWARD);
-                if (newFocus != null) {
-                    newFocus.requestFocus();
+                final View focusedChild = getDeepestFocusedChild();
+                if (!focusedChild.isFocusableInTouchMode()) {
+                    final View newFocus = focusSearch(FOCUS_FORWARD);
+                    if (newFocus != null) {
+                        newFocus.requestFocus();
+                    }
                 }
             }
         } else {
@@ -2485,10 +2502,6 @@
         }
         int descendantFocusability = getDescendantFocusability();
 
-        if (shouldBlockFocusForTouchscreen()) {
-            return false;
-        }
-
         switch (descendantFocusability) {
             case FOCUS_BLOCK_DESCENDANTS:
                 return super.requestFocus(direction, previouslyFocusedRect);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d9ad1f1..671aa10 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -468,13 +468,8 @@
 
                 // Compute surface insets required to draw at specified Z value.
                 // TODO: Use real shadow insets for a constant max Z.
-                if (view.isHardwareAccelerated()) {
-                    final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
-                    attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
-                } else {
-                    // Software accelerated windows can't use insets.
-                    attrs.surfaceInsets.setEmpty();
-                }
+                final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+                attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
 
                 CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
                 mTranslator = compatibilityInfo.getTranslator();
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 7e2d809..be7e0bc 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -92,10 +92,11 @@
         final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
         setThumb(thumb);
 
+        mThumbTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode);
+
         if (a.hasValue(R.styleable.SeekBar_thumbTint)) {
             mThumbTint = a.getColorStateList(R.styleable.SeekBar_thumbTint);
-            mThumbTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode);
             mHasThumbTint = true;
 
             applyThumbTint();
@@ -183,30 +184,6 @@
     }
 
     /**
-     * Applies a tint to the thumb drawable.
-     * <p>
-     * Subsequent calls to {@link #setThumb(Drawable)} will automatically
-     * mutate the drawable and apply the specified tint and tint mode using
-     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#SeekBar_thumbTint
-     * @attr ref android.R.styleable#SeekBar_thumbTintMode
-     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
-     */
-    private void setThumbTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mThumbTint = tint;
-        mThumbTintMode = tintMode;
-        mHasThumbTint = true;
-
-        applyThumbTint();
-    }
-
-    /**
      * Applies a tint to the thumb drawable. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
@@ -217,16 +194,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#SeekBar_thumbTint
-     * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+     * @see #getThumbTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setThumbTint(@Nullable ColorStateList tint) {
-        setThumbTint(tint, mThumbTintMode);
+        mThumbTint = tint;
+        mHasThumbTint = true;
+
+        applyThumbTint();
     }
 
     /**
      * @return the tint applied to the thumb drawable
      * @attr ref android.R.styleable#SeekBar_thumbTint
-     * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+     * @see #setThumbTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getThumbTint() {
@@ -240,17 +221,21 @@
      *
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
+     *
      * @attr ref android.R.styleable#SeekBar_thumbTintMode
-     * @see #setThumbTint(ColorStateList)
+     * @see #getThumbTintMode()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setThumbTint(mThumbTint, tintMode);
+        mThumbTintMode = tintMode;
+
+        applyThumbTint();
     }
 
     /**
      * @return the blending mode used to apply the tint to the thumb drawable
      * @attr ref android.R.styleable#SeekBar_thumbTintMode
-     * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+     * @see #setThumbTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getThumbTintMode() {
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 4aa2300..791151c 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -18,9 +18,12 @@
 
 import com.android.internal.R;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -41,8 +44,13 @@
  */
 public class CheckedTextView extends TextView implements Checkable {
     private boolean mChecked;
+
     private int mCheckMarkResource;
     private Drawable mCheckMarkDrawable;
+    private ColorStateList mCheckMarkTint = null;
+    private PorterDuff.Mode mCheckMarkTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasCheckMarkTint = false;
+
     private int mBasePadding;
     private int mCheckMarkWidth;
     private boolean mNeedRequestlayout;
@@ -74,6 +82,16 @@
             setCheckMarkDrawable(d);
         }
 
+        mCheckMarkTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.CompoundButton_buttonTintMode, -1), mCheckMarkTintMode);
+
+        if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
+            mCheckMarkTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
+            mHasCheckMarkTint = true;
+
+            applyCheckMarkTint();
+        }
+
         boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
         setChecked(checked);
 
@@ -153,6 +171,7 @@
 
             mCheckMarkWidth = d.getIntrinsicWidth();
             d.setState(getDrawableState());
+            applyCheckMarkTint();
         } else {
             mCheckMarkWidth = 0;
         }
@@ -163,6 +182,72 @@
         resolvePadding();
     }
 
+    /**
+     * Applies a tint to the check mark drawable. Does not modify the
+     * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setCheckMarkDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and
+     * tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#CheckedTextView_checkMarkTint
+     * @see #getCheckMarkTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setCheckMarkTint(@Nullable ColorStateList tint) {
+        mCheckMarkTint = tint;
+        mHasCheckMarkTint = true;
+
+        applyCheckMarkTint();
+    }
+
+    /**
+     * @return the tint applied to the check mark drawable
+     * @attr ref android.R.styleable#CheckedTextView_checkMarkTint
+     * @see #setCheckMarkTint(ColorStateList)
+     */
+    @Nullable
+    public ColorStateList getCheckMarkTint() {
+        return mCheckMarkTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setCheckMarkTint(ColorStateList)} to the check mark
+     * drawable. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
+     * @see #setCheckMarkTint(ColorStateList)
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setCheckMarkTintMode(@Nullable PorterDuff.Mode tintMode) {
+        mCheckMarkTintMode = tintMode;
+
+        applyCheckMarkTint();
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the check mark drawable
+     * @attr ref android.R.styleable#CheckedTextView_checkMarkTintMode
+     * @see #setCheckMarkTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getCheckMarkTintMode() {
+        return mCheckMarkTintMode;
+    }
+
+    private void applyCheckMarkTint() {
+        if (mCheckMarkDrawable != null && mHasCheckMarkTint) {
+            mCheckMarkDrawable = mCheckMarkDrawable.mutate();
+            mCheckMarkDrawable.setTint(mCheckMarkTint, mCheckMarkTintMode);
+        }
+    }
+
     @RemotableViewMethod
     @Override
     public void setVisibility(int visibility) {
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 9ba0fe1..a45777e 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -87,10 +87,11 @@
             setButtonDrawable(d);
         }
 
+        mButtonTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
+
         if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
             mButtonTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
-            mButtonTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
             mHasButtonTint = true;
 
             applyButtonTint();
@@ -238,52 +239,31 @@
     }
 
     /**
-     * Applies a tint to the button drawable.
-     * <p>
-     * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
-     * automatically mutate the drawable and apply the specified tint and tint
-     * mode using
-     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#CompoundButton_buttonTint
-     * @attr ref android.R.styleable#CompoundButton_buttonTintMode
-     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
-     */
-    private void setButtonTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mButtonTint = tint;
-        mButtonTintMode = tintMode;
-        mHasButtonTint = true;
-
-        applyButtonTint();
-    }
-
-    /**
      * Applies a tint to the button drawable. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
      * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
      * automatically mutate the drawable and apply the specified tint and tint
      * mode using
-     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
      *
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#CompoundButton_buttonTint
-     * @see #setButtonTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     * @see #setButtonTint(ColorStateList)
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setButtonTint(@Nullable ColorStateList tint) {
-        setButtonTint(tint, mButtonTintMode);
+        mButtonTint = tint;
+        mHasButtonTint = true;
+
+        applyButtonTint();
     }
 
     /**
      * @return the tint applied to the button drawable
      * @attr ref android.R.styleable#CompoundButton_buttonTint
-     * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+     * @see #setButtonTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getButtonTint() {
@@ -298,16 +278,19 @@
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#CompoundButton_buttonTintMode
-     * @see #setButtonTint(ColorStateList)
+     * @see #getButtonTintMode()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setButtonTint(mButtonTint, tintMode);
+        mButtonTintMode = tintMode;
+
+        applyButtonTint();
     }
 
     /**
      * @return the blending mode used to apply the tint to the button drawable
      * @attr ref android.R.styleable#CompoundButton_buttonTintMode
-     * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+     * @see #setButtonTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getButtonTintMode() {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 34f333e..81dfcbb 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -127,10 +127,11 @@
             setMeasureAllChildren(true);
         }
 
+        mForegroundTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
+
         if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
             mForegroundTint = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
-            mForegroundTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
             mHasForegroundTint = true;
 
             applyForegroundTint();
@@ -316,11 +317,6 @@
      */
     private void setForegroundTint(@Nullable ColorStateList tint,
             @Nullable PorterDuff.Mode tintMode) {
-        mForegroundTint = tint;
-        mForegroundTintMode = tintMode;
-        mHasForegroundTint = true;
-
-        applyForegroundTint();
     }
 
     /**
@@ -334,16 +330,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#FrameLayout_foregroundTint
-     * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #getForegroundTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setForegroundTint(@Nullable ColorStateList tint) {
-        setForegroundTint(tint, mForegroundTintMode);
+        mForegroundTint = tint;
+        mHasForegroundTint = true;
+
+        applyForegroundTint();
     }
 
     /**
      * @return the tint applied to the foreground drawable
      * @attr ref android.R.styleable#FrameLayout_foregroundTint
-     * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #setForegroundTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getForegroundTint() {
@@ -358,17 +358,20 @@
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
-     * @see #setForegroundTint(ColorStateList)
+     * @see #getForegroundTintMode()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setForegroundTint(mForegroundTint, tintMode);
+        mForegroundTintMode = tintMode;
+
+        applyForegroundTint();
     }
 
     /**
      * @return the blending mode used to apply the tint to the foreground
      *         drawable
      * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
-     * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #setForegroundTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getForegroundTintMode() {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index e97177d..0881f3e 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -168,10 +168,11 @@
             setScaleType(sScaleTypeArray[index]);
         }
 
+        mDrawableTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
+
         if (a.hasValue(R.styleable.ImageView_tint)) {
             mDrawableTint = a.getColorStateList(R.styleable.ImageView_tint);
-            mDrawableTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
             mHasDrawableTint = true;
 
             applyDrawableTint();
@@ -448,30 +449,6 @@
     }
 
     /**
-     * Applies a tint to the image drawable.
-     * <p>
-     * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
-     * mutate the drawable and apply the specified tint and tint mode using
-     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#ImageView_tint
-     * @attr ref android.R.styleable#ImageView_tintMode
-     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
-     */
-    private void setTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mDrawableTint = tint;
-        mDrawableTintMode = tintMode;
-        mHasDrawableTint = true;
-
-        applyDrawableTint();
-    }
-
-    /**
      * Applies a tint to the image drawable. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
@@ -482,16 +459,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#ImageView_tint
+     * @see #getTint()
      * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setTint(@Nullable ColorStateList tint) {
-        setTint(tint, mDrawableTintMode);
+        mDrawableTint = tint;
+        mHasDrawableTint = true;
+
+        applyDrawableTint();
     }
 
     /**
      * @return the tint applied to the image drawable
      * @attr ref android.R.styleable#ImageView_tint
-     * @see #setTint(ColorStateList, PorterDuff.Mode)
+     * @see #setTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getTint() {
@@ -506,16 +487,19 @@
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#ImageView_tintMode
-     * @see #setTint(ColorStateList)
+     * @see #getTintMode()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setTint(mDrawableTint, tintMode);
+        mDrawableTintMode = tintMode;
+
+        applyDrawableTint();
     }
 
     /**
      * @return the blending mode used to apply the tint to the image drawable
      * @attr ref android.R.styleable#ImageView_tintMode
-     * @see #setTint(ColorStateList, PorterDuff.Mode)
+     * @see #setTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getTintMode() {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 394b255..20c1aa4 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -326,48 +326,48 @@
 
         mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
 
+        mProgressTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.ProgressBar_progressBackgroundTintMode, -1), mProgressTintMode);
+
         if (a.hasValue(R.styleable.ProgressBar_progressTint)) {
             mProgressTint = a.getColorStateList(
                     R.styleable.ProgressBar_progressTint);
-            mProgressTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.ProgressBar_progressBackgroundTintMode, -1),
-                    mProgressTintMode);
             mHasProgressTint = true;
 
             applyProgressLayerTint(R.id.progress, mProgressTint,
                     mProgressTintMode, true);
         }
 
+        mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.ProgressBar_progressTintMode, -1), mProgressBackgroundTintMode);
+
         if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) {
             mProgressBackgroundTint = a.getColorStateList(
                     R.styleable.ProgressBar_progressBackgroundTint);
-            mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.ProgressBar_progressTintMode, -1),
-                    mProgressBackgroundTintMode);
             mHasProgressBackgroundTint = true;
 
             applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
                     mProgressBackgroundTintMode, false);
         }
 
+        mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.ProgressBar_secondaryProgressTintMode, -1), mSecondaryProgressTintMode);
+
         if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) {
             mSecondaryProgressTint = a.getColorStateList(
                     R.styleable.ProgressBar_secondaryProgressTint);
-            mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.ProgressBar_secondaryProgressTintMode, -1),
-                    mSecondaryProgressTintMode);
             mHasSecondaryProgressTint = true;
 
             applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
                     mSecondaryProgressTintMode, false);
         }
 
+        mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
+                R.styleable.ProgressBar_indeterminateTintMode, -1), mIndeterminateTintMode);
+
         if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
             mIndeterminateTint = a.getColorStateList(
                     R.styleable.ProgressBar_indeterminateTint);
-            mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.ProgressBar_indeterminateTintMode, -1),
-                    mIndeterminateTintMode);
             mHasIndeterminateTint = true;
 
             applyIndeterminateTint();
@@ -574,31 +574,6 @@
     }
 
     /**
-     * Applies a tint to the indeterminate drawable.
-     * <p>
-     * Subsequent calls to {@link #setVisibilminateDrawable(Drawable)} will
-     * automatically mutate the drawable and apply the specified tint and
-     * tint mode using
-     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#ProgressBar_indeterminateTint
-     * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
-     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
-     */
-    private void setIndeterminateTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mIndeterminateTint = tint;
-        mIndeterminateTintMode = tintMode;
-        mHasIndeterminateTint = true;
-
-        applyIndeterminateTint();
-    }
-
-    /**
      * Applies a tint to the indeterminate drawable. Does not modify the
      * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
@@ -610,16 +585,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#ProgressBar_indeterminateTint
-     * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+     * @see #getIndeterminateTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setIndeterminateTint(@Nullable ColorStateList tint) {
-        setIndeterminateTint(tint, mIndeterminateTintMode);
+        mIndeterminateTint = tint;
+        mHasIndeterminateTint = true;
+
+        applyIndeterminateTint();
     }
 
     /**
      * @return the tint applied to the indeterminate drawable
      * @attr ref android.R.styleable#ProgressBar_indeterminateTint
-     * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+     * @see #setIndeterminateTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getIndeterminateTint() {
@@ -635,15 +614,18 @@
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
      * @see #setIndeterminateTint(ColorStateList)
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setIndeterminateTint(mIndeterminateTint, tintMode);
+        mIndeterminateTintMode = tintMode;
+
+        applyIndeterminateTint();
     }
 
     /**
      * @return the blending mode used to apply the tint to the indeterminate drawable
      * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
-     * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+     * @see #setIndeterminateTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getIndeterminateTintMode() {
@@ -749,36 +731,6 @@
 
     /**
      * Applies a tint to the progress indicator, if one exists, or to the
-     * entire progress drawable otherwise.
-     * <p>
-     * The progress indicator should be specified as a layer with
-     * id {@link android.R.id#progress} in a {@link LayerDrawable}
-     * used as the progress drawable.
-     * <p>
-     * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
-     * automatically mutate the drawable and apply the specified tint and
-     * tint mode using
-     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#ProgressBar_progressTint
-     * @attr ref android.R.styleable#ProgressBar_progressTintMode
-     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
-     */
-    private void setProgressTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mProgressTint = tint;
-        mProgressTintMode = tintMode;
-        mHasProgressTint = true;
-
-        applyProgressLayerTint(R.id.progress, tint, tintMode, true);
-    }
-
-    /**
-     * Applies a tint to the progress indicator, if one exists, or to the
      * entire progress drawable otherwise. Does not modify the current tint
      * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
@@ -794,16 +746,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#ProgressBar_progressTint
-     * @see #setProgressTint(ColorStateList)
+     * @see #getProgressTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setProgressTint(@Nullable ColorStateList tint) {
-        setProgressTint(tint, mProgressTintMode);
+        mProgressTint = tint;
+        mHasProgressTint = true;
+
+        applyProgressLayerTint(R.id.progress, tint, mProgressTintMode, true);
     }
 
     /**
      * @return the tint applied to the progress drawable
      * @attr ref android.R.styleable#ProgressBar_progressTint
-     * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+     * @see #setProgressTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getProgressTint() {
@@ -818,16 +774,19 @@
      * @param tintMode the blending mode used to apply the tint, may be
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#ProgressBar_progressTintMode
-     * @see #setProgressTint(ColorStateList)
+     * @see #getProgressTintMode()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setProgressTint(mProgressTint, tintMode);
+        mProgressTintMode = tintMode;
+
+        applyProgressLayerTint(R.id.progress, mProgressTint, tintMode, true);
     }
 
     /**
      * @return the blending mode used to apply the tint to the progress drawable
      * @attr ref android.R.styleable#ProgressBar_progressTintMode
-     * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+     * @see #setProgressTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getProgressTintMode() {
@@ -835,35 +794,6 @@
     }
 
     /**
-     * Applies a tint to the progress background, if one exists.
-     * <p>
-     * The progress background must be specified as a layer with
-     * id {@link android.R.id#background} in a {@link LayerDrawable}
-     * used as the progress drawable.
-     * <p>
-     * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
-     * drawable contains a progress background will automatically mutate the
-     * drawable and apply the specified tint and tint mode using
-     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
-     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
-     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
-     */
-    private void setProgressBackgroundTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mProgressBackgroundTint = tint;
-        mProgressBackgroundTintMode = tintMode;
-        mHasProgressBackgroundTint = true;
-
-        applyProgressLayerTint(R.id.background, tint, tintMode, false);
-    }
-
-    /**
      * Applies a tint to the progress background, if one exists. Does not
      * modify the current tint mode, which is
      * {@link PorterDuff.Mode#SRC_ATOP} by default.
@@ -880,16 +810,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
-     * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #getProgressBackgroundTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setProgressBackgroundTint(@Nullable ColorStateList tint) {
-        setProgressBackgroundTint(tint, mProgressBackgroundTintMode);
+        mProgressBackgroundTint = tint;
+        mHasProgressBackgroundTint = true;
+
+        applyProgressLayerTint(R.id.background, tint, mProgressBackgroundTintMode, false);
     }
 
     /**
      * @return the tint applied to the progress background
      * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
-     * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #setProgressBackgroundTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getProgressBackgroundTint() {
@@ -905,16 +839,19 @@
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
      * @see #setProgressBackgroundTint(ColorStateList)
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setProgressBackgroundTint(mProgressBackgroundTint, tintMode);
+        mProgressBackgroundTintMode = tintMode;
+
+        applyProgressLayerTint(R.id.background, mProgressBackgroundTint, tintMode, false);
     }
 
     /**
      * @return the blending mode used to apply the tint to the progress
      *         background
      * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
-     * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+     * @see #setProgressBackgroundTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getProgressBackgroundTintMode() {
@@ -923,35 +860,6 @@
 
     /**
      * Applies a tint to the secondary progress indicator, if one exists.
-     * <p>
-     * The secondary progress indicator must be specified as a layer with
-     * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
-     * used as the progress drawable.
-     * <p>
-     * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
-     * drawable contains a secondary progress indicator will automatically
-     * mutate the drawable and apply the specified tint and tint mode using
-     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
-     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
-     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
-     */
-    private void setSecondaryProgressTint(@Nullable ColorStateList tint,
-            @Nullable PorterDuff.Mode tintMode) {
-        mSecondaryProgressTint = tint;
-        mSecondaryProgressTintMode = tintMode;
-        mHasSecondaryProgressTint = true;
-
-        applyProgressLayerTint(R.id.secondaryProgress, tint, tintMode, false);
-    }
-
-    /**
-     * Applies a tint to the secondary progress indicator, if one exists.
      * Does not modify the current tint mode, which is
      * {@link PorterDuff.Mode#SRC_ATOP} by default.
      * <p>
@@ -967,16 +875,20 @@
      * @param tint the tint to apply, may be {@code null} to clear tint
      *
      * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
-     * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+     * @see #getSecondaryProgressTint()
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setSecondaryProgressTint(@Nullable ColorStateList tint) {
-        setSecondaryProgressTint(tint, mSecondaryProgressTintMode);
+        mSecondaryProgressTint = tint;
+        mHasSecondaryProgressTint = true;
+
+        applyProgressLayerTint(R.id.secondaryProgress, tint, mSecondaryProgressTintMode, false);
     }
 
     /**
      * @return the tint applied to the secondary progress drawable
      * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
-     * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+     * @see #setSecondaryProgressTint(ColorStateList)
      */
     @Nullable
     public ColorStateList getSecondaryProgressTint() {
@@ -993,16 +905,19 @@
      *                 {@code null} to clear tint
      * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
      * @see #setSecondaryProgressTint(ColorStateList)
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
      */
     public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
-        setSecondaryProgressTint(mSecondaryProgressTint, tintMode);
+        mSecondaryProgressTintMode = tintMode;
+
+        applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint, tintMode, false);
     }
 
     /**
      * @return the blending mode used to apply the tint to the secondary
      *         progress drawable
      * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
-     * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+     * @see #setSecondaryProgressTintMode(PorterDuff.Mode)
      */
     @Nullable
     public PorterDuff.Mode getSecondaryProgressTintMode() {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index fdb305d..e215517 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -17,6 +17,9 @@
 package com.android.internal.app;
 
 import android.app.Activity;
+import android.app.usage.PackageUsageStats;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
 import android.os.AsyncTask;
 import android.widget.AbsListView;
 import android.widget.GridView;
@@ -54,12 +57,13 @@
 import android.widget.TextView;
 import com.android.internal.widget.ResolverDrawerLayout;
 
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -69,7 +73,7 @@
  */
 public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {
     private static final String TAG = "ResolverActivity";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private int mLaunchedFromUid;
     private ResolveListAdapter mAdapter;
@@ -84,6 +88,10 @@
     private int mMaxColumns;
     private int mLastSelected = ListView.INVALID_POSITION;
 
+    private UsageStatsManager mUsm;
+    private UsageStats mStats;
+    private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
+
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override public void onSomePackagesChanged() {
@@ -170,7 +178,7 @@
     protected void onCreate(Bundle savedInstanceState, Intent intent,
             CharSequence title, Intent[] initialIntents,
             List<ResolveInfo> rList, boolean alwaysUseOption) {
-        onCreate(savedInstanceState, intent, title, initialIntents, rList, alwaysUseOption);
+        onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
     }
 
     protected void onCreate(Bundle savedInstanceState, Intent intent,
@@ -185,6 +193,12 @@
             mLaunchedFromUid = -1;
         }
         mPm = getPackageManager();
+        mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+
+        final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
+        mStats = mUsm.getRecentStatsSince(sinceTime);
+        Log.d(TAG, "sinceTime=" + sinceTime);
+
         mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
 
         mPackageMonitor.register(this, getMainLooper(), false);
@@ -672,7 +686,7 @@
                 for (int i=1; i<N; i++) {
                     ResolveInfo ri = currentResolveList.get(i);
                     if (DEBUG) Log.v(
-                        "ResolveListActivity",
+                        TAG,
                         r0.activityInfo.name + "=" +
                         r0.priority + "/" + r0.isDefault + " vs " +
                         ri.activityInfo.name + "=" +
@@ -689,8 +703,8 @@
                     }
                 }
                 if (N > 1) {
-                    ResolveInfo.DisplayNameComparator rComparator =
-                            new ResolveInfo.DisplayNameComparator(mPm);
+                    Comparator<ResolveInfo> rComparator =
+                            new ResolverComparator(ResolverActivity.this);
                     Collections.sort(currentResolveList, rComparator);
                 }
                 // First put the initial items at the top.
@@ -703,8 +717,7 @@
                         ActivityInfo ai = ii.resolveActivityInfo(
                                 getPackageManager(), 0);
                         if (ai == null) {
-                            Log.w("ResolverActivity", "No activity found for "
-                                    + ii);
+                            Log.w(TAG, "No activity found for " + ii);
                             continue;
                         }
                         ResolveInfo ri = new ResolveInfo();
@@ -939,5 +952,52 @@
             mTargetView.setImageDrawable(info.displayIcon);
         }
     }
+
+    class ResolverComparator implements Comparator<ResolveInfo> {
+        private final Collator mCollator;
+
+        public ResolverComparator(Context context) {
+            mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
+        }
+
+        @Override
+        public int compare(ResolveInfo lhs, ResolveInfo rhs) {
+            // We want to put the one targeted to another user at the end of the dialog.
+            if (lhs.targetUserId != UserHandle.USER_CURRENT) {
+                return 1;
+            }
+            if (lhs.targetUserId != UserHandle.USER_CURRENT) {
+                return -1;
+            }
+
+            if (mStats != null) {
+                final long timeDiff =
+                        getPackageTimeSpent(rhs.activityInfo.packageName) -
+                        getPackageTimeSpent(lhs.activityInfo.packageName);
+
+                if (timeDiff != 0) {
+                    return timeDiff > 0 ? 1 : -1;
+                }
+            }
+
+            CharSequence  sa = lhs.loadLabel(mPm);
+            if (sa == null) sa = lhs.activityInfo.name;
+            CharSequence  sb = rhs.loadLabel(mPm);
+            if (sb == null) sb = rhs.activityInfo.name;
+
+            return mCollator.compare(sa.toString(), sb.toString());
+        }
+
+        private long getPackageTimeSpent(String packageName) {
+            if (mStats != null) {
+                final PackageUsageStats stats = mStats.getPackage(packageName);
+                if (stats != null) {
+                    return stats.getTotalTimeSpent();
+                }
+
+            }
+            return 0;
+        }
+    }
 }
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index cc481e1..d60f787 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -18,6 +18,8 @@
 
 import android.Manifest;
 import android.app.ActivityManagerNative;
+import android.app.AlarmClockInfo;
+import android.app.AlarmManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
 import android.appwidget.AppWidgetManager;
@@ -1170,13 +1172,9 @@
      * @return A formatted string of the next alarm (for showing on the lock screen),
      *   or null if there is no next alarm.
      */
-    public String getNextAlarm() {
-        String nextAlarm = Settings.System.getStringForUser(mContentResolver,
-                Settings.System.NEXT_ALARM_FORMATTED, UserHandle.USER_CURRENT);
-        if (nextAlarm == null || TextUtils.isEmpty(nextAlarm)) {
-            return null;
-        }
-        return nextAlarm;
+    public AlarmClockInfo getNextAlarm() {
+        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        return alarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
     }
 
     private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0e22174..f65aab5 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,6 +122,7 @@
 	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 d17f46c..9f832b0 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,6 +3,7 @@
 
 #include "SkCamera.h"
 
+#include "Canvas.h"
 #include "GraphicsJNI.h"
 
 static jfieldID gNativeInstanceFieldID;
@@ -95,10 +96,10 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas((SkCanvas*)native_canvas);
+    v->applyToCanvas(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
deleted file mode 100644
index 6254f3d..0000000
--- a/core/jni/android/graphics/Canvas.cpp
+++ /dev/null
@@ -1,1330 +0,0 @@
-/*
- * 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
new file mode 100644
index 0000000..fde22ee
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.h
@@ -0,0 +1,137 @@
+/*
+ * 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 uint16_t* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const SkPaint& paint,
+            TypefaceImpl* typeface) = 0;
+    virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+            int posCount, const SkPaint& paint) = 0;
+    virtual void drawTextOnPath(const uint16_t* glyphs, 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 5cc2b95..320c259 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -4,6 +4,7 @@
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
 
+#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkMath.h"
@@ -364,7 +365,7 @@
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
     jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
-    SkCanvas* c = getNativeCanvas(canvasHandle);
+    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
     SkASSERT(c);
     return c;
 }
@@ -385,7 +386,6 @@
     SkASSERT(env->IsInstanceOf(paint, gPaint_class));
     jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID);
     android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle);
-    SkASSERT(p);
     return p;
 }
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8150edf..28a6edb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -47,7 +47,6 @@
     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 ab5bdb0..e82e8a6 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,6 +23,7 @@
 
 #include <Caches.h>
 
+#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
@@ -119,7 +120,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
@@ -138,7 +139,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
         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 bc0c25f..d214575 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,12 +36,13 @@
     }
 }
 
-SkCanvas* Picture::beginRecording(int width, int height) {
+Canvas* Picture::beginRecording(int width, int height) {
     mPicture.reset(NULL);
     mRecorder.reset(new SkPictureRecorder);
     mWidth = width;
     mHeight = height;
-    return mRecorder->beginRecording(width, height, NULL, 0);
+    SkCanvas* canvas = mRecorder->beginRecording(width, height, NULL, 0);
+    return Canvas::create_canvas(canvas);
 }
 
 void Picture::endRecording() {
@@ -93,14 +94,14 @@
     }
 }
 
-void Picture::draw(SkCanvas* canvas) {
+void Picture::draw(Canvas* 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);
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas->getSkCanvas());
     }
 }
 
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index abb0403..a2e5d4a 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -22,14 +22,13 @@
 #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
@@ -38,7 +37,7 @@
 public:
     explicit Picture(const Picture* src = NULL);
 
-    SkCanvas* beginRecording(int width, int height);
+    Canvas* beginRecording(int width, int height);
 
     void endRecording();
 
@@ -50,7 +49,7 @@
 
     void serialize(SkWStream* stream) const;
 
-    void draw(SkCanvas* canvas);
+    void draw(Canvas* canvas);
 
 private:
     int mWidth;
diff --git a/core/jni/android/graphics/SkiaCanvas.cpp b/core/jni/android/graphics/SkiaCanvas.cpp
new file mode 100644
index 0000000..a4e0d28
--- /dev/null
+++ b/core/jni/android/graphics/SkiaCanvas.cpp
@@ -0,0 +1,773 @@
+/*
+ * 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 uint16_t* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface);
+    virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+            int posCount, const SkPaint& paint);
+    virtual void drawTextOnPath(const uint16_t* glyphs, 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 uint16_t* 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(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 uint16_t* 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 uint16_t* glyphs, int count, const SkPath& path,
+        float hOffset, float vOffset, const SkPaint& paint) {
+    mCanvas->drawTextOnPathHV(glyphs, 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 3812c27..9436a47 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,8 +132,9 @@
         jint pageWidth, jint pageHeight,
         jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
-    return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
-            contentLeft, contentTop, contentRight, contentBottom));
+    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
+            contentLeft, contentTop, contentRight, contentBottom);
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
 }
 
 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
new file mode 100644
index 0000000..46eccdd
--- /dev/null
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -0,0 +1,647 @@
+/*
+ * 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);
+    get_canvas(canvasHandle)->drawText(jchars + index, 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);
+    get_canvas(canvasHandle)->drawText(jchars + start, 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);
+    get_canvas(canvasHandle)->drawText(jchars + contextIndex, 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);
+    get_canvas(canvasHandle)->drawText(jchars + contextStart, 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;
+
+    get_canvas(canvasHandle)->drawPosText(jchars + index, 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;
+
+    get_canvas(canvasHandle)->drawPosText(jchars , 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(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 uint16_t* 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(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);
+
+    drawTextOnPath(get_canvas(canvasHandle), jchars + 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);
+    int count = env->GetStringLength(text);
+
+    drawTextOnPath(get_canvas(canvasHandle), jchars, 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 f827907..eb8f6dd 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) {
-    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
     Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
     SkASSERT(canvas);
     SkASSERT(picture);
@@ -84,12 +84,7 @@
 static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
                                                      jint w, jint h) {
     Picture* pict = reinterpret_cast<Picture*>(pictHandle);
-    // 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();
+    Canvas* canvas = pict->beginRecording(w, h);
     return reinterpret_cast<jlong>(canvas);
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fc55116..7f97726 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2461,14 +2461,15 @@
     <permission android:name="android.permission.PACKAGE_USAGE_STATS"
         android:label="@string/permlab_pkgUsageStats"
         android:description="@string/permdesc_pkgUsageStats"
-        android:protectionLevel="signature|system" />
+        android:protectionLevel="signature|system|development|appop" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
     <!-- @SystemApi Allows an application to collect battery statistics -->
     <permission android:name="android.permission.BATTERY_STATS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:label="@string/permlab_batteryStats"
         android:description="@string/permdesc_batteryStats"
-        android:protectionLevel="signature|system" />
+        android:protectionLevel="signature|system|development" />
 
     <!-- @SystemApi Allows an application to control the backup and restore process.
     <p>Not for use by third-party applications.
@@ -2796,12 +2797,12 @@
                  android:icon="@drawable/ic_launcher_android"
                  android:supportsRtl="true">
         <activity android:name="com.android.internal.app.ChooserActivity"
-                android:theme="@style/Theme.Holo.Dialog.Alert"
+                android:theme="@style/Theme.DeviceDefault.Resolver"
                 android:finishOnCloseSystemDialogs="true"
                 android:excludeFromRecents="true"
-                android:multiprocess="true"
                 android:documentLaunchMode="never"
-                android:relinquishTaskIdentity="true">
+                android:relinquishTaskIdentity="true"
+                android:process=":ui">
             <intent-filter>
                 <action android:name="android.intent.action.CHOOSER" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
index 7c77a45..b9a81be 100644
--- a/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
index bc6da21..3682629 100644
--- a/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
index 7a72295..f3d06fe 100644
--- a/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
index 483931f..f0e7db8 100644
--- a/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
index 16c376a..7174b67 100644
--- a/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
index e3bd904..46dad22 100644
--- a/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
index a22f352..661d5f0 100644
--- a/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
index b0504e0..d7696c3 100644
--- a/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml
index 86cec8f..eaf5e45 100644
--- a/core/res/res/drawable/edit_text_material.xml
+++ b/core/res/res/drawable/edit_text_material.xml
@@ -14,20 +14,25 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?attr/colorControlActivated">
-    <item>
-        <selector>
-            <item android:state_enabled="false">
-                <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
-                    android:tint="?attr/colorControlNormal"
-                    android:alpha="?attr/disabledAlpha" />
-            </item>
-            <item>
-                <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
-                    android:tint="?attr/colorControlNormal" />
-            </item>
-        </selector>
-    </item>
-    <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
-</ripple>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="@dimen/control_inset_material"
+    android:insetTop="@dimen/control_inset_material"
+    android:insetBottom="@dimen/control_inset_material"
+    android:insetRight="@dimen/control_inset_material">
+    <ripple android:color="?attr/colorControlActivated">
+        <item>
+            <selector>
+                <item android:state_enabled="false">
+                    <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+                        android:tint="?attr/colorControlNormal"
+                        android:alpha="?attr/disabledAlpha" />
+                </item>
+                <item>
+                    <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+                        android:tint="?attr/colorControlNormal" />
+                </item>
+            </selector>
+        </item>
+        <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
+    </ripple>
+</inset>
diff --git a/core/res/res/drawable/switch_thumb_material_anim.xml b/core/res/res/drawable/switch_thumb_material_anim.xml
index e7baa2c..67e460a 100644
--- a/core/res/res/drawable/switch_thumb_material_anim.xml
+++ b/core/res/res/drawable/switch_thumb_material_anim.xml
@@ -14,41 +14,62 @@
      limitations under the License.
 -->
 
-<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
-    <item android:state_enabled="false" android:state_checked="true">
-        <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:constantSize="true">
+    <item
+        android:state_enabled="false"
+        android:state_checked="true">
+        <bitmap
+            android:src="@drawable/btn_switch_to_on_mtrl_014"
+            android:gravity="center"
+            android:tint="?attr/colorControlActivated"
+            android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_enabled="false">
-        <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+        <bitmap
+            android:src="@drawable/btn_switch_to_on_mtrl_000"
+            android:gravity="center"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
     </item>
-    <item android:state_checked="true" android:id="@+id/on">
-        <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+    <item
+        android:state_checked="true"
+        android:id="@+id/on">
+        <bitmap
+            android:src="@drawable/btn_switch_to_on_mtrl_014"
+            android:gravity="center"
+            android:tint="?attr/colorControlActivated" />
     </item>
     <item android:id="@+id/off">
-        <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
+        <bitmap
+            android:src="@drawable/btn_switch_to_on_mtrl_000"
+            android:gravity="center"
+            android:tint="?attr/colorControlNormal" />
     </item>
-    <transition android:fromId="@+id/off" android:toId="@+id/on">
+    <transition
+        android:fromId="@+id/off"
+        android:toId="@+id/on">
         <animation-list>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_001" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_002" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_003" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_004" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_005" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_on_mtrl_006" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
                 <bitmap android:src="@drawable/btn_switch_to_on_mtrl_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
@@ -100,28 +121,28 @@
                 <bitmap android:src="@drawable/btn_switch_to_off_mtrl_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_007" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_008" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_009" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_010" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_011" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_012" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_013" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_switch_to_off_mtrl_014" android:gravity="center" android:tint="?attr/colorControlNormal" />
             </item>
         </animation-list>
     </transition>
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 75b0c3d..7aa9a72 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -43,6 +43,7 @@
         <!-- Activity name -->
         <TextView android:id="@android:id/text1"
                   android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:fontFamily="sans-serif-condensed"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:gravity="center"
@@ -51,6 +52,7 @@
         <!-- Extended activity info to distinguish between duplicate activity names -->
         <TextView android:id="@android:id/text2"
                   android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:fontFamily="sans-serif-condensed"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:gravity="center"
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 5acb588..b3a3478 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -35,6 +35,7 @@
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
         android:transitionName="android:action_bar"
+        android:touchscreenBlocksFocus="true"
         android:gravity="top">
         <com.android.internal.widget.ActionBarView
             android:id="@+id/action_bar"
@@ -53,5 +54,6 @@
                   android:layout_height="wrap_content"
                   style="?attr/actionBarSplitStyle"
                   android:visibility="gone"
+                  android:touchscreenBlocksFocus="true"
                   android:gravity="center"/>
 </com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
index 56815f8..039e89f 100644
--- a/core/res/res/layout/screen_toolbar.xml
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -35,6 +35,7 @@
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
         android:transitionName="android:action_bar"
+        android:touchscreenBlocksFocus="true"
         android:gravity="top">
         <Toolbar
             android:id="@+id/action_bar"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f57d61d..8d1b397 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3114,6 +3114,28 @@
         <attr name="checked" />
         <!-- Drawable used for the check mark graphic. -->
         <attr name="checkMark" format="reference"/>
+        <!-- Tint to apply to the check mark. -->
+        <attr name="checkMarkTint" format="color" />
+        <!-- Blending mode used to apply the check mark tint. -->
+        <attr name="checkMarkTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="EditText">
     </declare-styleable>
@@ -3579,7 +3601,7 @@
                  result to valid color values. Saturate(S + D) -->
             <enum name="add" value="16" />
         </attr>
-        <!-- Tint to apply to the indepterminate progress indicator. -->
+        <!-- Tint to apply to the indeterminate progress indicator. -->
         <attr name="indeterminateTint" format="color" />
         <!-- Blending mode used to apply the indeterminate progress indicator tint. -->
         <attr name="indeterminateTintMode">
@@ -4887,9 +4909,9 @@
             <enum name="disabled" value="-1" />
             <!-- Replicates the edge color. -->
             <enum name="clamp" value="0" />
-            <!-- Repeats the bitmap in both direction. -->
+            <!-- Repeats the bitmap horizontally. -->
             <enum name="repeat" value="1" />
-            <!-- Repeats the shader's image horizontally and vertically, alternating
+            <!-- Repeats the shader's image horizontally, alternating
                  mirror images so that adjacent images always seam. -->
             <enum name="mirror" value="2" />
         </attr>
@@ -4900,9 +4922,9 @@
             <enum name="disabled" value="-1" />
             <!-- Replicates the edge color. -->
             <enum name="clamp" value="0" />
-            <!-- Repeats the bitmap in both direction. -->
+            <!-- Repeats the bitmap vertically. -->
             <enum name="repeat" value="1" />
-            <!-- Repeats the shader's image horizontally and vertically, alternating
+            <!-- Repeats the shader's image vertically, alternating
                  mirror images so that adjacent images always seam. -->
             <enum name="mirror" value="2" />
         </attr>
@@ -7191,19 +7213,6 @@
              the settings for this service. -->
         <attr name="settingsActivity" />
         <!-- Type of this service. -->
-        <attr name="tvInputType">
-            <!-- Should be in sync with constant values defined in
-                 {@link android.media.tv.TvInputInfo}. -->
-
-            <!-- Virtual input (default) -->
-            <enum name="virtual" value="0" />
-            <!-- HDMI -->
-            <enum name="hdmi" value="1" />
-            <!-- Built-in tuner -->
-            <enum name="tuner" value="2" />
-            <!-- Pass-through -->
-            <enum name="passthrough" value="3" />
-        </attr>
     </declare-styleable>
 
     <declare-styleable name="ResolverDrawerLayout">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 021edc5..7311a60 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -212,6 +212,9 @@
         <!-- Additional flag from base permission type: this permission can also
              (optionally) be granted to development applications. -->
         <flag name="development" value="0x20" />
+        <!-- Additional flag from base permission type: this permission is closely
+             associated with an app op for controlling access. -->
+        <flag name="appop" value="0x40" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
@@ -905,7 +908,7 @@
         <enum name="persistRootOnly" value="0" />
         <!-- If this activity forms the root of a task then that task will not be persisted
              across reboots -->
-        <enum name="doNotPersist" value="1" />
+        <enum name="persistNever" value="1" />
         <!-- If this activity forms the root of a task then the task and this activity will
              be persisted across reboots. If the activity above this activity is also
              tagged with the attribute <code>"persist"</code> then it will be persisted as well.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e4484ad..5036c24 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2097,7 +2097,6 @@
   <public type="attr" name="isGame" id="0x10103f4" />
   <public type="attr" name="allowEmbedded" id="0x10103f5" />
   <public type="attr" name="setupActivity" id="0x10103f6"/>
-  <public type="attr" name="tvInputType" id="0x10103f7"/>
 
 <!-- ===============================================================
      Resources added in version 21 of the platform
@@ -2240,7 +2239,7 @@
   <public type="attr" name="launchTaskBehindSourceAnimation" />
   <!-- Attribute specified in a restriction entry to denote the type of restriction. -->
   <public type="attr" name="restrictionType" />
-
+  <!-- For the DatePicker -->
   <public type="attr" name="dateSelectorDayOfWeekBackgroundColor" />
   <public type="attr" name="dateSelectorDayOfWeekTextAppearance" />
   <public type="attr" name="dateSelectorBackgroundColor" />
@@ -2251,7 +2250,6 @@
   <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" />
@@ -2265,8 +2263,9 @@
   <public type="attr" name="amPmTextColor" />
   <public type="attr" name="amPmUnselectedBackgroundColor" />
   <public type="attr" name="amPmSelectedBackgroundColor" />
-
   <public type="attr" name="searchKeyphraseRecognitionFlags" />
+  <public type="attr" name="checkMarkTint" />
+  <public type="attr" name="checkMarkTintMode" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index b9cacd7..2dccaf2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1203,6 +1203,7 @@
         <item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
         <item name="collapseIcon">?attr/homeAsUpIndicator</item>
         <item name="contentInsetStart">16dp</item>
+        <item name="touchscreenBlocksFocus">true</item>
     </style>
 
     <style name="Widget.Toolbar.Button.Navigation" parent="Widget">
diff --git a/docs/html/google/play-services/ads.jd b/docs/html/google/play-services/ads.jd
index ed346c6..52f643e 100644
--- a/docs/html/google/play-services/ads.jd
+++ b/docs/html/google/play-services/ads.jd
@@ -59,6 +59,11 @@
       <h4>Flexible and powerful tools </h4>
       <p>Filters and controls help you manage your ads. If you want to use multiple ad
       networks, you can do that too, with free ad network mediation.</p>
+
+      <h4>Ad Policy Compliant</h4>
+      <p>The Google Mobile Ads SDK available in Google Play services is compliant with
+      Google Play Ad Poilcy regarding the usage of
+      <a href="{@docRoot}google/play-services/id.html">advertising ID</a>.</p>
   </div>
 
   <div class="col-6 normal-links">
diff --git a/docs/html/google/play-services/id.jd b/docs/html/google/play-services/id.jd
index db50c7f..43d1809 100644
--- a/docs/html/google/play-services/id.jd
+++ b/docs/html/google/play-services/id.jd
@@ -55,6 +55,19 @@
   </div>
 </div>
 
+<p class="caution" style=
+"background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">
+  As a reminder, please note that starting <strong>1 August 2014</strong>, new
+  apps and app updates distributed through Google Play must use the advertising
+  ID in lieu of any other persistent identifiers for any advertising purposes,
+  on devices that support the advertising ID.<br>
+  <br>
+  To learn how to check your app's compliance through the Developer Console, or
+  for details on the associated developer policy changes, please see the
+  <a href=
+  "https://support.google.com/googleplay/android-developer/answer/6048248">Advertising
+  ID topic</a> in the Google Play developer help center.
+</p>
 
 <h2 id="get_started">Using the Advertising ID</h2>
 
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index c4d5083..f0e93b9 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -453,7 +453,7 @@
 created to handle a new intent.  However, if the target task already has an
 existing instance of the activity at the top of its stack, that instance
 will receive the new intent (in an
-<code>{@link android.app.Activity#onNewIntent onNewIntent()}</code> call);
+{@link android.app.Activity#onNewIntent onNewIntent()} call);
 a new instance is not created.
 In other circumstances &mdash; for example, if an existing instance of the
 "{@code singleTop}" activity is in the target task, but not at the top of
@@ -461,6 +461,22 @@
 &mdash; a new instance would be created and pushed on the stack.
 </p>
 
+<p>Similarly, if you
+<a href="{@docRoot}training/implementing-navigation/ancestral.html">navigate
+up</a> to an activity on the current stack, the behavior is determined by the
+parent activity's launch mode. If the parent activity has launch mode {@code
+singleTop} (or the <code>up</code> intent contains {@link
+android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}), the parent is brought to the
+top of the stack, and its state is preserved. The navigation intent is received
+by the parent activity's {@link android.app.Activity#onNewIntent onNewIntent()}
+method.  If the parent activity has launch mode {@code standard} (and the
+<code>up</code> intent does not contain {@link
+android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}), the current activity and its
+parent are both popped off the stack, and a new instance of the parent activity
+is created to receive the navigation intent.
+</p>
+
+
 <p>
 The "{@code singleTask}" and "{@code singleInstance}" modes also differ from
 each other in only one respect:  A "{@code singleTask}" activity allows other
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index a4302ef..62d18d6 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -57,9 +57,9 @@
     "type":"video"
   },
   {
-    "title":"5 Tips for Succeeding",
+    "title":"Plan for Success",
     "titleFriendly":"",
-    "summary":"See inspirational ways of using technology in the classroom.",
+    "summary":"5 tips from developers on creating great EDU apps.",
     "url":"https://www.youtube.com/watch?v=Eh2adsAyTKc",
     "group":"",
     "keywords": [],
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 6e98f5e..2bbdfc8c 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -306,6 +306,9 @@
   <p>
   Android Studio is a new IDE powered by IntelliJ that provides new features and improvements
   over ADT. It's currently in beta but will be the official Android IDE once it's ready.</p>
+  <p>
+  If you're a new Android developer, you should consider starting with Android Studio, because the
+  ADT plugin for Eclipse is no longer in active development.</p>
   <p style="margin: 0;">
   <a href="/sdk/installing/studio.html">Learn more about Android Studio</a></p>
   </div>
diff --git a/docs/html/training/implementing-navigation/ancestral.jd b/docs/html/training/implementing-navigation/ancestral.jd
index 57251c1..8aa0fd6 100644
--- a/docs/html/training/implementing-navigation/ancestral.jd
+++ b/docs/html/training/implementing-navigation/ancestral.jd
@@ -133,7 +133,25 @@
 navigateUpFromSameTask()}. When you call this method, it finishes the current activity and
 starts (or resumes) the appropriate parent activity.
 If the target parent activity is in the task's back stack, it is brought
-forward as defined by {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}.</p>
+forward. The way it is brought forward depends on whether the parent activity
+is able to handle an {@link
+android.app.Activity#onNewIntent onNewIntent()} call:</p>
+
+<ul>
+    <li>If the parent activity has launch mode <a
+    href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+    &lt;singleTop>}</a>, or the <code>up</code> intent contains {@link
+    android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}, the parent activity is
+    brought to the top of the stack, and receives the intent through its
+    {@link android.app.Activity#onNewIntent onNewIntent()} method.</li>
+
+    <li>If the parent activity has launch mode <a
+    href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+    &lt;standard>}</a>, and the <code>up</code> intent does not contain
+    {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}, the parent activity
+    is popped off the stack, and a new instance of that activity is created on
+    top of the stack to receive the intent.</li>
+</ul>
 
 <p>For example:</p>
 
@@ -157,7 +175,6 @@
 navigating <em>Up</em> should create a new task that belongs to your app, which
 requires that you create a new back stack.</p>
 
-
 <h3 id="BuildBackStack">Navigate up with a new back stack</h3>
 
 <p>If your activity provides any <a
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 72e2056..ef4b260 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 = initCanvas(nativeCanvas);
+        mNativeCanvasWrapper = 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) {
-        native_drawRGB(mNativeCanvasWrapper, r, g, b);
+        drawColor(Color.rgb(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) {
-        native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
+        drawColor(Color.argb(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);
+        native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
     /**
@@ -1301,13 +1301,28 @@
      */
     public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
             @Nullable Paint paint) {
-        if (dst == null) {
-            throw new NullPointerException();
-        }
-        throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
-                          paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
-    }
+      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);
+  }
 
     /**
      * Draw the specified bitmap, scaling/translating automatically to fill
@@ -1337,8 +1352,23 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
-                paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+        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);
     }
 
     /**
@@ -1866,7 +1896,6 @@
     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);
@@ -1919,11 +1948,6 @@
     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,
@@ -1965,16 +1989,9 @@
                                                  int screenDensity,
                                                  int bitmapDensity);
     private native void native_drawBitmap(long nativeCanvas, long nativeBitmap,
-                                                 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);
+            float srcLeft, float srcTop, float srcRight, float srcBottom,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            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/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index db5c8e3..525e01d 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -386,6 +386,7 @@
      *
      * @see #setTileModeY(android.graphics.Shader.TileMode)
      * @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
+     * @attr ref android.R.styleable#BitmapDrawable_tileModeX
      */
     public void setTileModeX(Shader.TileMode mode) {
         setTileModeXY(mode, mBitmapState.mTileModeY);
@@ -401,6 +402,7 @@
      *
      * @see #setTileModeX(android.graphics.Shader.TileMode)
      * @see #setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)
+     * @attr ref android.R.styleable#BitmapDrawable_tileModeY
      */
     public final void setTileModeY(Shader.TileMode mode) {
         setTileModeXY(mBitmapState.mTileModeX, mode);
diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java
index f327c4e..963b604 100644
--- a/location/java/android/location/GpsClock.java
+++ b/location/java/android/location/GpsClock.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 /**
  * A class containing a GPS clock timestamp.
@@ -26,44 +27,65 @@
  * @hide
  */
 public class GpsClock implements Parcelable {
-    // mandatory parameters
-    private long mTimeInNs;
+    private static final String TAG = "GpsClock";
 
-    // optional parameters
-    private boolean mHasLeapSecond;
+    // The following enumerations must be in sync with the values declared in gps.h
+
+    /**
+     * The type of the time stored is not available or it is unknown.
+     */
+    public static final byte TYPE_UNKNOWN = 0;
+
+    /**
+     * The source of the time value reported by this class is the 'Local Hardware Clock'.
+     */
+    public static final byte TYPE_LOCAL_HW_TIME = 1;
+
+    /**
+     * The source of the time value reported by this class is the 'GPS time' derived from
+     * satellites (epoch = Jan 6, 1980).
+     */
+    public static final byte TYPE_GPS_TIME = 2;
+
+    private static final short HAS_NO_FLAGS = 0;
+    private static final short HAS_LEAP_SECOND = (1<<0);
+    private static final short HAS_TIME_UNCERTAINTY = (1<<1);
+    private static final short HAS_FULL_BIAS = (1<<2);
+    private static final short HAS_BIAS = (1<<3);
+    private static final short HAS_BIAS_UNCERTAINTY = (1<<4);
+    private static final short HAS_DRIFT = (1<<5);
+    private static final short HAS_DRIFT_UNCERTAINTY = (1<<6);
+
+    // End enumerations in sync with gps.h
+
+    private short mFlags;
     private short mLeapSecond;
-    private boolean mHasTimeUncertaintyInNs;
+    private byte mType;
+    private long mTimeInNs;
     private double mTimeUncertaintyInNs;
-    private boolean mHasBiasInNs;
+    private long mFullBiasInNs;
     private double mBiasInNs;
-    private boolean mHasBiasUncertaintyInNs;
     private double mBiasUncertaintyInNs;
-    private boolean mHasDriftInNsPerSec;
     private double mDriftInNsPerSec;
-    private boolean mHasDriftUncertaintyInNsPerSec;
     private double mDriftUncertaintyInNsPerSec;
 
     GpsClock() {
-        reset();
+        initialize();
     }
 
     /**
      * Sets all contents to the values stored in the provided object.
      */
     public void set(GpsClock clock) {
-        mTimeInNs = clock.mTimeInNs;
-
-        mHasLeapSecond = clock.mHasLeapSecond;
+        mFlags = clock.mFlags;
         mLeapSecond = clock.mLeapSecond;
-        mHasTimeUncertaintyInNs = clock.mHasTimeUncertaintyInNs;
+        mType = clock.mType;
+        mTimeInNs = clock.mTimeInNs;
         mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
-        mHasBiasInNs = clock.mHasBiasInNs;
+        mFullBiasInNs = clock.mFullBiasInNs;
         mBiasInNs = clock.mBiasInNs;
-        mHasBiasUncertaintyInNs = clock.mHasBiasUncertaintyInNs;
         mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs;
-        mHasDriftInNsPerSec = clock.mHasDriftInNsPerSec;
         mDriftInNsPerSec = clock.mDriftInNsPerSec;
-        mHasDriftUncertaintyInNsPerSec = clock.mHasDriftUncertaintyInNsPerSec;
         mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec;
     }
 
@@ -71,25 +93,61 @@
      * Resets all the contents to its original state.
      */
     public void reset() {
-        mTimeInNs = Long.MIN_VALUE;
+        initialize();
+    }
 
-        resetLeapSecond();
-        resetTimeUncertaintyInNs();
-        resetBiasInNs();
-        resetBiasUncertaintyInNs();
-        resetDriftInNsPerSec();
-        resetDriftUncertaintyInNsPerSec();
+    /**
+     * Gets the type of time reported by {@link #getTimeInNs()}.
+     */
+    public byte getType() {
+        return mType;
+    }
+
+    /**
+     * Sets the type of time reported.
+     */
+    public void setType(byte value) {
+        switch (value) {
+            case TYPE_UNKNOWN:
+            case TYPE_GPS_TIME:
+            case TYPE_LOCAL_HW_TIME:
+                mType = value;
+                break;
+            default:
+                Log.d(TAG, "Sanitizing invalid 'type': " + value);
+                mType = TYPE_UNKNOWN;
+                break;
+        }
+    }
+
+    /**
+     * Gets a string representation of the 'type'.
+     * For internal and logging use only.
+     */
+    private String getTypeString() {
+        switch (mType) {
+            case TYPE_UNKNOWN:
+                return "Unknown";
+            case TYPE_GPS_TIME:
+                return "GpsTime";
+            case TYPE_LOCAL_HW_TIME:
+                return "LocalHwClock";
+            default:
+                return "<Invalid>";
+        }
     }
 
     /**
      * Returns true if {@link #getLeapSecond()} is available, false otherwise.
      */
     public boolean hasLeapSecond() {
-        return mHasLeapSecond;
+        return isFlagSet(HAS_LEAP_SECOND);
     }
 
     /**
      * Gets the leap second associated with the clock's time.
+     * The sign of the value is defined by the following equation:
+     *      utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000
      *
      * The value is only available if {@link #hasLeapSecond()} is true.
      */
@@ -101,7 +159,7 @@
      * Sets the leap second associated with the clock's time.
      */
     public void setLeapSecond(short leapSecond) {
-        mHasLeapSecond = true;
+        setFlag(HAS_LEAP_SECOND);
         mLeapSecond = leapSecond;
     }
 
@@ -109,13 +167,25 @@
      * Resets the leap second associated with the clock's time.
      */
     public void resetLeapSecond() {
-        mHasLeapSecond = false;
+        resetFlag(HAS_LEAP_SECOND);
         mLeapSecond = Short.MIN_VALUE;
     }
 
     /**
-     * Gets the GPS clock Time in nanoseconds; it represents the uncorrected receiver's GPS time
-     * since 0000Z, January 6, 1980; this is, including {@link #getBiasInNs()}.
+     * Gets the GPS receiver internal clock value in nanoseconds.
+     * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the
+     * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}).
+     * {@link #getType()} defines the time reported.
+     *
+     * For 'local hardware clock' this value is expected to be monotonically increasing during the
+     * reporting session. The real GPS time can be derived by compensating
+     * {@link #getFullBiasInNs()} (when it is available) from this value.
+     *
+     * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS
+     * receiver can achieve. {@link #getTimeUncertaintyInNs()} should be available when GPS time is
+     * specified.
+     *
+     * Sub-nanosecond accuracy can be provided by means of {@link #getBiasInNs()}.
      * The reported time includes {@link #getTimeUncertaintyInNs()}.
      */
     public long getTimeInNs() {
@@ -123,7 +193,7 @@
     }
 
     /**
-     * Sets the GPS clock Time in nanoseconds.
+     * Sets the GPS receiver internal clock in nanoseconds.
      */
     public void setTimeInNs(long timeInNs) {
         mTimeInNs = timeInNs;
@@ -133,11 +203,12 @@
      * Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise.
      */
     public boolean hasTimeUncertaintyInNs() {
-        return mHasTimeUncertaintyInNs;
+        return isFlagSet(HAS_TIME_UNCERTAINTY);
     }
 
     /**
      * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds.
+     * The uncertainty is represented as an absolute (single sided) value.
      *
      * The value is only available if {@link #hasTimeUncertaintyInNs()} is true.
      */
@@ -149,7 +220,7 @@
      * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
      */
     public void setTimeUncertaintyInNs(double timeUncertaintyInNs) {
-        mHasTimeUncertaintyInNs = true;
+        setFlag(HAS_TIME_UNCERTAINTY);
         mTimeUncertaintyInNs = timeUncertaintyInNs;
     }
 
@@ -157,34 +228,73 @@
      * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
      */
     public void resetTimeUncertaintyInNs() {
-        mHasTimeUncertaintyInNs = false;
+        resetFlag(HAS_TIME_UNCERTAINTY);
         mTimeUncertaintyInNs = Double.NaN;
     }
 
     /**
+     * Returns true if {@link @getFullBiasInNs()} is available, false otherwise.
+     */
+    public boolean hasFullBiasInNs() {
+        return isFlagSet(HAS_FULL_BIAS);
+    }
+
+    /**
+     * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
+     * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
+     *
+     * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
+     * the clock for GPS time.
+     * {@link #getBiasUncertaintyInNs()} should be used for quality check.
+     *
+     * The sign of the value is defined by the following equation:
+     *      true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
+     *
+     * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
+     * The value is onl available if {@link #hasFullBiasInNs()} is true.
+     */
+    public long getFullBiasInNs() {
+        return mFullBiasInNs;
+    }
+
+    /**
+     * Sets the full bias in nanoseconds.
+     */
+    public void setFullBiasInNs(long value) {
+        setFlag(HAS_FULL_BIAS);
+        mFullBiasInNs = value;
+    }
+
+    /**
+     * Resets the full bias in nanoseconds.
+     */
+    public void resetFullBiasInNs() {
+        resetFlag(HAS_FULL_BIAS);
+        mFullBiasInNs = Long.MIN_VALUE;
+    }
+
+    /**
      * Returns true if {@link #getBiasInNs()} is available, false otherwise.
      */
     public boolean hasBiasInNs() {
-        return mHasBiasInNs;
+        return isFlagSet(HAS_BIAS);
     }
 
     /**
-     * Gets the clock's Bias in nanoseconds.
-     * The sign of the value (if available), is defined by the following equation:
-     *      true time = time - bias.
+     * Gets the clock's sub-nanosecond bias.
      * The reported bias includes {@link #getBiasUncertaintyInNs()}.
      *
      * The value is only available if {@link #hasBiasInNs()} is true.
      */
-    public Double getBiasInNs() {
+    public double getBiasInNs() {
         return mBiasInNs;
     }
 
     /**
-     * Sets the clock's Bias in nanoseconds.
+     * Sets the sub-nanosecond bias.
      */
     public void setBiasInNs(double biasInNs) {
-        mHasBiasInNs = true;
+        setFlag(HAS_BIAS);
         mBiasInNs = biasInNs;
     }
 
@@ -192,7 +302,7 @@
      * Resets the clock's Bias in nanoseconds.
      */
     public void resetBiasInNs() {
-        mHasBiasInNs = false;
+        resetFlag(HAS_BIAS);
         mBiasInNs = Double.NaN;
     }
 
@@ -200,7 +310,7 @@
      * Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise.
      */
     public boolean hasBiasUncertaintyInNs() {
-        return mHasBiasUncertaintyInNs;
+        return isFlagSet(HAS_BIAS_UNCERTAINTY);
     }
 
     /**
@@ -216,7 +326,7 @@
      * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
      */
     public void setBiasUncertaintyInNs(double biasUncertaintyInNs) {
-        mHasBiasUncertaintyInNs = true;
+        setFlag(HAS_BIAS_UNCERTAINTY);
         mBiasUncertaintyInNs = biasUncertaintyInNs;
     }
 
@@ -224,7 +334,7 @@
      * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
      */
     public void resetBiasUncertaintyInNs() {
-        mHasBiasUncertaintyInNs = false;
+        resetFlag(HAS_BIAS_UNCERTAINTY);
         mBiasUncertaintyInNs = Double.NaN;
     }
 
@@ -232,7 +342,7 @@
      * Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise.
      */
     public boolean hasDriftInNsPerSec() {
-        return mHasDriftInNsPerSec;
+        return isFlagSet(HAS_DRIFT);
     }
 
     /**
@@ -250,7 +360,7 @@
      * Sets the clock's Drift in nanoseconds per second.
      */
     public void setDriftInNsPerSec(double driftInNsPerSec) {
-        mHasDriftInNsPerSec = true;
+        setFlag(HAS_DRIFT);
         mDriftInNsPerSec = driftInNsPerSec;
     }
 
@@ -258,7 +368,7 @@
      * Resets the clock's Drift in nanoseconds per second.
      */
     public void resetDriftInNsPerSec() {
-        mHasDriftInNsPerSec = false;
+        resetFlag(HAS_DRIFT);
         mDriftInNsPerSec = Double.NaN;
     }
 
@@ -266,7 +376,7 @@
      * Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise.
      */
     public boolean hasDriftUncertaintyInNsPerSec() {
-        return mHasDriftUncertaintyInNsPerSec;
+        return isFlagSet(HAS_DRIFT_UNCERTAINTY);
     }
 
     /**
@@ -282,7 +392,7 @@
      * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
      */
     public void setDriftUncertaintyInNsPerSec(double driftUncertaintyInNsPerSec) {
-        mHasDriftUncertaintyInNsPerSec = true;
+        setFlag(HAS_DRIFT_UNCERTAINTY);
         mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec;
     }
 
@@ -290,7 +400,7 @@
      * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
      */
     public void resetDriftUncertaintyInNsPerSec() {
-        mHasDriftUncertaintyInNsPerSec = false;
+        resetFlag(HAS_DRIFT_UNCERTAINTY);
         mDriftUncertaintyInNsPerSec = Double.NaN;
     }
 
@@ -298,19 +408,16 @@
         @Override
         public GpsClock createFromParcel(Parcel parcel) {
             GpsClock gpsClock = new GpsClock();
-            gpsClock.mTimeInNs = parcel.readLong();
 
-            gpsClock.mHasLeapSecond = parcel.readInt() != 0;
+            gpsClock.mFlags = (short) parcel.readInt();
             gpsClock.mLeapSecond = (short) parcel.readInt();
-            gpsClock.mHasTimeUncertaintyInNs = parcel.readInt() != 0;
+            gpsClock.mType = parcel.readByte();
+            gpsClock.mTimeInNs = parcel.readLong();
             gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
-            gpsClock.mHasBiasInNs = parcel.readInt() != 0;
+            gpsClock.mFullBiasInNs = parcel.readLong();
             gpsClock.mBiasInNs = parcel.readDouble();
-            gpsClock.mHasBiasUncertaintyInNs = parcel.readInt() != 0;
             gpsClock.mBiasUncertaintyInNs = parcel.readDouble();
-            gpsClock.mHasDriftInNsPerSec = parcel.readInt() != 0;
             gpsClock.mDriftInNsPerSec = parcel.readDouble();
-            gpsClock.mHasDriftUncertaintyInNsPerSec = parcel.readInt() != 0;
             gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble();
 
             return gpsClock;
@@ -323,19 +430,15 @@
     };
 
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeLong(mTimeInNs);
-
-        parcel.writeInt(mHasLeapSecond ? 1 : 0);
+        parcel.writeInt(mFlags);
         parcel.writeInt(mLeapSecond);
-        parcel.writeInt(mHasTimeUncertaintyInNs ? 1 : 0);
+        parcel.writeByte(mType);
+        parcel.writeLong(mTimeInNs);
         parcel.writeDouble(mTimeUncertaintyInNs);
-        parcel.writeInt(mHasBiasInNs ? 1 : 0);
+        parcel.writeLong(mFullBiasInNs);
         parcel.writeDouble(mBiasInNs);
-        parcel.writeInt(mHasBiasUncertaintyInNs ? 1 : 0);
         parcel.writeDouble(mBiasUncertaintyInNs);
-        parcel.writeInt(mHasDriftInNsPerSec ? 1 : 0);
         parcel.writeDouble(mDriftInNsPerSec);
-        parcel.writeInt(mHasDriftUncertaintyInNsPerSec ? 1 : 0);
         parcel.writeDouble(mDriftUncertaintyInNsPerSec);
     }
 
@@ -346,37 +449,64 @@
 
     @Override
     public String toString() {
-        final String format = "   %-15s = %-25s   %-26s = %s\n";
+        final String format = "   %-15s = %s\n";
+        final String formatWithUncertainty = "   %-15s = %-25s   %-26s = %s\n";
         StringBuilder builder = new StringBuilder("GpsClock:\n");
 
-        builder.append(String.format(
-                format,
-                "LeapSecond",
-                mHasLeapSecond ? mLeapSecond : null,
-                "",
-                ""));
+        builder.append(String.format(format, "Type", getTypeString()));
+
+        builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
 
         builder.append(String.format(
-                format,
+                formatWithUncertainty,
                 "TimeInNs",
                 mTimeInNs,
                 "TimeUncertaintyInNs",
-                mHasTimeUncertaintyInNs ? mTimeUncertaintyInNs : null));
+                hasTimeUncertaintyInNs() ? mTimeUncertaintyInNs : null));
 
         builder.append(String.format(
                 format,
+                "FullBiasInNs",
+                hasFullBiasInNs() ? mFullBiasInNs : null));
+
+        builder.append(String.format(
+                formatWithUncertainty,
                 "BiasInNs",
-                mHasBiasInNs ? mBiasInNs : null,
+                hasBiasInNs() ? mBiasInNs : null,
                 "BiasUncertaintyInNs",
-                mHasBiasUncertaintyInNs ? mBiasUncertaintyInNs : null));
+                hasBiasUncertaintyInNs() ? mBiasUncertaintyInNs : null));
 
         builder.append(String.format(
-                format,
+                formatWithUncertainty,
                 "DriftInNsPerSec",
-                mHasDriftInNsPerSec ? mDriftInNsPerSec : null,
+                hasDriftInNsPerSec() ? mDriftInNsPerSec : null,
                 "DriftUncertaintyInNsPerSec",
-                mHasDriftUncertaintyInNsPerSec ? mDriftUncertaintyInNsPerSec : null));
+                hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
 
         return builder.toString();
     }
+
+    private void initialize() {
+        mFlags = HAS_NO_FLAGS;
+        resetLeapSecond();
+        setType(TYPE_UNKNOWN);
+        setTimeInNs(Long.MIN_VALUE);
+        resetTimeUncertaintyInNs();
+        resetBiasInNs();
+        resetBiasUncertaintyInNs();
+        resetDriftInNsPerSec();
+        resetDriftUncertaintyInNsPerSec();
+    }
+
+    private void setFlag(short flag) {
+        mFlags |= flag;
+    }
+
+    private void resetFlag(short flag) {
+        mFlags &= ~flag;
+    }
+
+    private boolean isFlagSet(short flag) {
+        return (mFlags & flag) == flag;
+    }
 }
diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java
index e5a8c8c..2272ac3 100644
--- a/location/java/android/location/GpsMeasurement.java
+++ b/location/java/android/location/GpsMeasurement.java
@@ -28,143 +28,175 @@
 public class GpsMeasurement implements Parcelable {
     private static final String TAG = "GpsMeasurement";
 
-    // mandatory parameters
+    private int mFlags;
     private byte mPrn;
-    private long mLocalTimeInNs;
+    private double mTimeOffsetInNs;
+    private short mState;
     private long mReceivedGpsTowInNs;
     private double mCn0InDbHz;
     private double mPseudorangeRateInMetersPerSec;
     private double mPseudorangeRateUncertaintyInMetersPerSec;
+    private short mAccumulatedDeltaRangeState;
     private double mAccumulatedDeltaRangeInMeters;
     private double mAccumulatedDeltaRangeUncertaintyInMeters;
-
-    // optional parameters
-    private boolean mHasPseudorangeInMeters;
     private double mPseudorangeInMeters;
-    private boolean mHasPseudorangeUncertaintyInMeters;
     private double mPseudorangeUncertaintyInMeters;
-    private boolean mHasCodePhaseInChips;
     private double mCodePhaseInChips;
-    private boolean mHasCodePhaseUncertaintyInChips;
     private double mCodePhaseUncertaintyInChips;
-    private boolean mHasCarrierFrequencyInHz;
     private float mCarrierFrequencyInHz;
-    private boolean mHasCarrierCycles;
     private long mCarrierCycles;
-    private boolean mHasCarrierPhase;
     private double mCarrierPhase;
-    private boolean mHasCarrierPhaseUncertainty;
     private double mCarrierPhaseUncertainty;
-    private short mLossOfLock;
-    private boolean mHasBitNumber;
+    private byte mLossOfLock;
     private short mBitNumber;
-    private boolean mHasTimeFromLastBitInNs;
-    private long mTimeFromLastBitInNs;
-    private boolean mHasDopplerShiftInHz;
+    private short mTimeFromLastBitInMs;
     private double mDopplerShiftInHz;
-    private boolean mHasDopplerShiftUncertaintyInHz;
     private double mDopplerShiftUncertaintyInHz;
-    private short mMultipathIndicator;
-    private boolean mHasSnrInDb;
+    private byte mMultipathIndicator;
     private double mSnrInDb;
-    private boolean mHasElevationInDeg;
     private double mElevationInDeg;
-    private boolean mHasElevationUncertaintyInDeg;
     private double mElevationUncertaintyInDeg;
-    private boolean mHasAzimuthInDeg;
     private double mAzimuthInDeg;
-    private boolean mHasAzimuthUncertaintyInDeg;
     private double mAzimuthUncertaintyInDeg;
     private boolean mUsedInFix;
 
     // The following enumerations must be in sync with the values declared in gps.h
 
+    private static final int HAS_NO_FLAGS = 0;
+    private static final int HAS_SNR = (1<<0);
+    private static final int HAS_ELEVATION = (1<<1);
+    private static final int HAS_ELEVATION_UNCERTAINTY = (1<<2);
+    private static final int HAS_AZIMUTH = (1<<3);
+    private static final int HAS_AZIMUTH_UNCERTAINTY = (1<<4);
+    private static final int HAS_PSEUDORANGE = (1<<5);
+    private static final int HAS_PSEUDORANGE_UNCERTAINTY = (1<<6);
+    private static final int HAS_CODE_PHASE = (1<<7);
+    private static final int HAS_CODE_PHASE_UNCERTAINTY = (1<<8);
+    private static final int HAS_CARRIER_FREQUENCY = (1<<9);
+    private static final int HAS_CARRIER_CYCLES = (1<<10);
+    private static final int HAS_CARRIER_PHASE = (1<<11);
+    private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
+    private static final int HAS_BIT_NUMBER = (1<<13);
+    private static final int HAS_TIME_FROM_LAST_BIT = (1<<14);
+    private static final int HAS_DOPPLER_SHIFT = (1<<15);
+    private static final int HAS_DOPPLER_SHIFT_UNCERTAINTY = (1<<16);
+
     /**
      * The indicator is not available or it is unknown.
      */
-    public static final short LOSS_OF_LOCK_UNKNOWN = 0;
+    public static final byte LOSS_OF_LOCK_UNKNOWN = 0;
 
     /**
      * The measurement does not present any indication of 'loss of lock'.
      */
-    public static final short LOSS_OF_LOCK_OK = 1;
+    public static final byte LOSS_OF_LOCK_OK = 1;
 
     /**
      * 'Loss of lock' detected between the previous and current observation: cycle slip possible.
      */
-    public static final short LOSS_OF_LOCK_CYCLE_SLIP = 2;
+    public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2;
 
     /**
      * The indicator is not available or it is unknown.
      */
-    public static final short MULTIPATH_INDICATOR_UNKNOWN = 0;
+    public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0;
 
     /**
      * The measurement has been indicated to use multi-path.
      */
-    public static final short MULTIPATH_INDICATOR_DETECTED = 1;
+    public static final byte MULTIPATH_INDICATOR_DETECTED = 1;
 
     /**
      * The measurement has been indicated not tu use multi-path.
      */
-    public static final short MULTIPATH_INDICATOR_NOT_USED = 2;
+    public static final byte MULTIPATH_INDICATOR_NOT_USED = 2;
+
+    /**
+     * The state of GPS receiver the measurement is invalid or unknown.
+     */
+    public static final short STATE_UNKNOWN = 0;
+
+    /**
+     * The state of the GPS receiver is ranging code lock.
+     */
+    public static final short STATE_CODE_LOCK = (1<<0);
+
+    /**
+     * The state of the GPS receiver is in bit sync.
+     */
+    public static final short STATE_BIT_SYNC = (1<<1);
+
+    /**
+     *The state of the GPS receiver is in sub-frame sync.
+     */
+    public static final short STATE_SUBFRAME_SYNC = (1<<2);
+
+    /**
+     * The state of the GPS receiver has TOW decoded.
+     */
+    public static final short STATE_TOW_DECODED = (1<<3);
+
+    /**
+     * The state of the 'Accumulated Delta Range' is invalid or unknown.
+     */
+    public static final short ADR_STATE_UNKNOWN = 0;
+
+    /**
+     * The state of the 'Accumulated Delta Range' is valid.
+     */
+    public static final short ADR_STATE_VALID = (1<<0);
+
+    /**
+     * The state of the 'Accumulated Delta Range' has detected a reset.
+     */
+    public static final short ADR_STATE_RESET = (1<<1);
+
+    /**
+     * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+     */
+    public static final short ADR_STATE_CYCLE_SLIP = (1<<2);
 
     // End enumerations in sync with gps.h
 
     GpsMeasurement() {
-        reset();
+        initialize();
     }
 
     /**
      * Sets all contents to the values stored in the provided object.
      */
     public void set(GpsMeasurement measurement) {
+        mFlags = measurement.mFlags;
         mPrn = measurement.mPrn;
-        mLocalTimeInNs = measurement.mLocalTimeInNs;
+        mTimeOffsetInNs = measurement.mTimeOffsetInNs;
+        mState = measurement.mState;
         mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
         mCn0InDbHz = measurement.mCn0InDbHz;
         mPseudorangeRateInMetersPerSec = measurement.mPseudorangeRateInMetersPerSec;
         mPseudorangeRateUncertaintyInMetersPerSec =
                 measurement.mPseudorangeRateUncertaintyInMetersPerSec;
+        mAccumulatedDeltaRangeState = measurement.mAccumulatedDeltaRangeState;
         mAccumulatedDeltaRangeInMeters = measurement.mAccumulatedDeltaRangeInMeters;
         mAccumulatedDeltaRangeUncertaintyInMeters =
                 measurement.mAccumulatedDeltaRangeUncertaintyInMeters;
-
-        mHasPseudorangeInMeters = measurement.mHasPseudorangeInMeters;
         mPseudorangeInMeters = measurement.mPseudorangeInMeters;
-        mHasPseudorangeUncertaintyInMeters = measurement.mHasPseudorangeUncertaintyInMeters;
         mPseudorangeUncertaintyInMeters = measurement.mPseudorangeUncertaintyInMeters;
-        mHasCodePhaseInChips = measurement.mHasCodePhaseInChips;
         mCodePhaseInChips = measurement.mCodePhaseInChips;
-        mHasCodePhaseUncertaintyInChips = measurement.mHasCodePhaseUncertaintyInChips;
         mCodePhaseUncertaintyInChips = measurement.mCodePhaseUncertaintyInChips;
-        mHasCarrierFrequencyInHz = measurement.mHasCarrierFrequencyInHz;
         mCarrierFrequencyInHz = measurement.mCarrierFrequencyInHz;
-        mHasCarrierCycles = measurement.mHasCarrierCycles;
         mCarrierCycles = measurement.mCarrierCycles;
-        mHasCarrierPhase = measurement.mHasCarrierPhase;
         mCarrierPhase = measurement.mCarrierPhase;
-        mHasCarrierPhaseUncertainty = measurement.mHasCarrierPhaseUncertainty;
         mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
         mLossOfLock = measurement.mLossOfLock;
-        mHasBitNumber = measurement.mHasBitNumber;
         mBitNumber = measurement.mBitNumber;
-        mHasTimeFromLastBitInNs = measurement.mHasTimeFromLastBitInNs;
-        mTimeFromLastBitInNs = measurement.mTimeFromLastBitInNs;
-        mHasDopplerShiftInHz = measurement.mHasDopplerShiftInHz;
+        mTimeFromLastBitInMs = measurement.mTimeFromLastBitInMs;
         mDopplerShiftInHz = measurement.mDopplerShiftInHz;
-        mHasDopplerShiftUncertaintyInHz = measurement.mHasDopplerShiftUncertaintyInHz;
         mDopplerShiftUncertaintyInHz = measurement.mDopplerShiftUncertaintyInHz;
         mMultipathIndicator = measurement.mMultipathIndicator;
-        mHasSnrInDb = measurement.mHasSnrInDb;
         mSnrInDb = measurement.mSnrInDb;
-        mHasElevationInDeg = measurement.mHasElevationInDeg;
         mElevationInDeg = measurement.mElevationInDeg;
-        mHasElevationUncertaintyInDeg = measurement.mHasElevationUncertaintyInDeg;
         mElevationUncertaintyInDeg = measurement.mElevationUncertaintyInDeg;
-        mHasAzimuthInDeg = measurement.mHasAzimuthInDeg;
         mAzimuthInDeg = measurement.mAzimuthInDeg;
-        mHasAzimuthUncertaintyInDeg = measurement.mHasAzimuthUncertaintyInDeg;
         mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg;
         mUsedInFix = measurement.mUsedInFix;
     }
@@ -173,35 +205,7 @@
      * Resets all the contents to its original state.
      */
     public void reset() {
-        mPrn = Byte.MIN_VALUE;
-        mLocalTimeInNs = Long.MIN_VALUE;
-        mReceivedGpsTowInNs = Long.MIN_VALUE;
-        mCn0InDbHz = Double.MIN_VALUE;
-        mPseudorangeRateInMetersPerSec = Double.MIN_VALUE;
-        mPseudorangeRateUncertaintyInMetersPerSec = Double.MIN_VALUE;
-        mAccumulatedDeltaRangeInMeters = Double.MIN_VALUE;
-        mAccumulatedDeltaRangeUncertaintyInMeters = Double.MIN_VALUE;
-
-        resetPseudorangeInMeters();
-        resetPseudorangeUncertaintyInMeters();
-        resetCodePhaseInChips();
-        resetCodePhaseUncertaintyInChips();
-        resetCarrierFrequencyInHz();
-        resetCarrierCycles();
-        resetCarrierPhase();
-        resetCarrierPhaseUncertainty();
-        setLossOfLock(LOSS_OF_LOCK_UNKNOWN);
-        resetBitNumber();
-        resetTimeFromLastBitInNs();
-        resetDopplerShiftInHz();
-        resetDopplerShiftUncertaintyInHz();
-        setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
-        resetSnrInDb();
-        resetElevationInDeg();
-        resetElevationUncertaintyInDeg();
-        resetAzimuthInDeg();
-        resetAzimuthUncertaintyInDeg();
-        setUsedInFix(false);
+        initialize();
     }
 
     /**
@@ -220,22 +224,87 @@
     }
 
     /**
-     * Gets the local (hardware) time at which the measurement was taken in nanoseconds.
+     * Gets the time offset at which the measurement was taken in nanoseconds.
+     * The reference receiver's time is specified by {@link GpsClock#getTimeInNs()} and should be
+     * interpreted in the same way as indicated by {@link GpsClock#getType()}.
+     *
+     * The sign of this value is given by the following equation:
+     *      measurement time = time_ns + time_offset_ns
+     *
+     * The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
+     * accuracy.
      */
-    public long getLocalTimeInNs() {
-        return mLocalTimeInNs;
+    public double getTimeOffsetInNs() {
+        return mTimeOffsetInNs;
     }
 
     /**
-     * Sets the measurement's local (hardware) time in nanoseconds.
+     * Sets the time offset at which the measurement was taken in nanoseconds.
      */
-    public void setLocalTimeInNs(long value) {
-        mLocalTimeInNs = value;
+    public void setTimeOffsetInNs(double value) {
+        mTimeOffsetInNs = value;
     }
 
     /**
-     * Gets the received GPS Time-of-Week in nanoseconds.
+     * Gets per-satellite sync state.
+     * It represents the current sync state for the associated satellite.
+     *
+     * This value helps interpret {@link #getReceivedGpsTowInNs()}.
+     */
+    public short getState() {
+        return mState;
+    }
+
+    /**
+     * Sets the sync state.
+     */
+    public void setState(short value) {
+        switch (value) {
+            case STATE_UNKNOWN:
+            case STATE_BIT_SYNC:
+            case STATE_CODE_LOCK:
+            case STATE_SUBFRAME_SYNC:
+            case STATE_TOW_DECODED:
+                mState = value;
+                break;
+            default:
+                Log.d(TAG, "Sanitizing invalid 'sync state': " + value);
+                mState = STATE_UNKNOWN;
+                break;
+        }
+    }
+
+    /**
+     * Gets a string representation of the 'sync state'.
+     * For internal and logging use only.
+     */
+    private String getStateString() {
+        switch (mState) {
+            case STATE_UNKNOWN:
+                return "Unknown";
+            case STATE_BIT_SYNC:
+                return "BitSync";
+            case STATE_CODE_LOCK:
+                return "CodeLock";
+            case STATE_SUBFRAME_SYNC:
+                return "SubframeSync";
+            case STATE_TOW_DECODED:
+                return "TowDecoded";
+            default:
+                return "<Invalid>";
+        }
+    }
+
+    /**
+     * Gets the received GPS Time-of-Week at the measurement time, in nanoseconds.
      * The value is relative to the beginning of the current GPS week.
+     *
+     * Given {@link #getState()} of the GPS receiver, the range of this field can be:
+     *      Searching           : [ 0           ]   : {@link #STATE_UNKNOWN} is set
+     *      Ranging code lock   : [ 0    1 ms   ]   : {@link #STATE_CODE_LOCK} is set
+     *      Bit sync            : [ 0   20 ms   ]   : {@link #STATE_BIT_SYNC} is set
+     *      Subframe sync       : [ 0    6 ms   ]   : {@link #STATE_SUBFRAME_SYNC} is set
+     *      TOW decoded         : [ 0    1 week ]   : {@link #STATE_TOW_DECODED} is set
      */
     public long getReceivedGpsTowInNs() {
         return mReceivedGpsTowInNs;
@@ -296,8 +365,56 @@
     }
 
     /**
+     * Gets 'Accumulated Delta Range' state.
+     * It indicates whether {@link #getAccumulatedDeltaRangeInMeters()} is reset or there is a
+     * cycle slip (indicating 'loss of lock').
+     */
+    public short getAccumulatedDeltaRangeState() {
+        return mAccumulatedDeltaRangeState;
+    }
+
+    /**
+     * Sets the 'Accumulated Delta Range' state.
+     */
+    public void setAccumulatedDeltaRangeState(short value) {
+        switch (value) {
+            case ADR_STATE_UNKNOWN:
+            case ADR_STATE_VALID:
+            case ADR_STATE_RESET:
+            case ADR_STATE_CYCLE_SLIP:
+                mAccumulatedDeltaRangeState = value;
+                break;
+            default:
+                Log.d(TAG, "Sanitizing invalid 'Accumulated Delta Range state': " + value);
+                mAccumulatedDeltaRangeState = ADR_STATE_UNKNOWN;
+                break;
+        }
+    }
+
+    /**
+     * Gets a string representation of the 'Accumulated Delta Range state'.
+     * For internal and logging use only.
+     */
+    private String getAccumulatedDeltaRangeStateString() {
+        switch (mAccumulatedDeltaRangeState) {
+            case ADR_STATE_UNKNOWN:
+                return "Unknown";
+            case ADR_STATE_VALID:
+                return "Valid";
+            case ADR_STATE_RESET:
+                return "Reset";
+            case ADR_STATE_CYCLE_SLIP:
+                return "CycleSlip";
+            default:
+                return "<Invalid>";
+        }
+    }
+
+    /**
      * Gets the accumulated delta range since the last channel reset, in meters.
      * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}.
+     *
+     * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
      */
     public double getAccumulatedDeltaRangeInMeters() {
         return mAccumulatedDeltaRangeInMeters;
@@ -320,6 +437,8 @@
 
     /**
      * Sets the accumulated delta range's uncertainty (1-sigma) in meters.
+     *
+     * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
      */
     public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) {
         mAccumulatedDeltaRangeUncertaintyInMeters = value;
@@ -329,7 +448,7 @@
      * Returns true if {@link #getPseudorangeInMeters()} is available, false otherwise.
      */
     public boolean hasPseudorangeInMeters() {
-        return mHasPseudorangeInMeters;
+        return isFlagSet(HAS_PSEUDORANGE);
     }
 
     /**
@@ -346,7 +465,7 @@
      * Sets the Pseudo-range in meters.
      */
     public void setPseudorangeInMeters(double value) {
-        mHasPseudorangeInMeters = true;
+        setFlag(HAS_PSEUDORANGE);
         mPseudorangeInMeters = value;
     }
 
@@ -354,7 +473,7 @@
      * Resets the Pseudo-range in meters.
      */
     public void resetPseudorangeInMeters() {
-        mHasPseudorangeInMeters = false;
+        resetFlag(HAS_PSEUDORANGE);
         mPseudorangeInMeters = Double.NaN;
     }
 
@@ -362,7 +481,7 @@
      * Returns true if {@link #getPseudorangeUncertaintyInMeters()} is available, false otherwise.
      */
     public boolean hasPseudorangeUncertaintyInMeters() {
-        return mHasPseudorangeUncertaintyInMeters;
+        return isFlagSet(HAS_PSEUDORANGE_UNCERTAINTY);
     }
 
     /**
@@ -380,7 +499,7 @@
      * Sets the pseudo-range's uncertainty (1-Sigma) in meters.
      */
     public void setPseudorangeUncertaintyInMeters(double value) {
-        mHasPseudorangeUncertaintyInMeters = true;
+        setFlag(HAS_PSEUDORANGE_UNCERTAINTY);
         mPseudorangeUncertaintyInMeters = value;
     }
 
@@ -388,7 +507,7 @@
      * Resets the pseudo-range's uncertainty (1-Sigma) in meters.
      */
     public void resetPseudorangeUncertaintyInMeters() {
-        mHasPseudorangeUncertaintyInMeters = false;
+        resetFlag(HAS_PSEUDORANGE_UNCERTAINTY);
         mPseudorangeUncertaintyInMeters = Double.NaN;
     }
 
@@ -396,7 +515,7 @@
      * Returns true if {@link #getCodePhaseInChips()} is available, false otherwise.
      */
     public boolean hasCodePhaseInChips() {
-        return mHasCodePhaseInChips;
+        return isFlagSet(HAS_CODE_PHASE);
     }
 
     /**
@@ -415,7 +534,7 @@
      * Sets the Code-phase in chips.
      */
     public void setCodePhaseInChips(double value) {
-        mHasCodePhaseInChips = true;
+        setFlag(HAS_CODE_PHASE);
         mCodePhaseInChips = value;
     }
 
@@ -423,7 +542,7 @@
      * Resets the Code-phase in chips.
      */
     public void resetCodePhaseInChips() {
-        mHasCodePhaseInChips = false;
+        resetFlag(HAS_CODE_PHASE);
         mCodePhaseInChips = Double.NaN;
     }
 
@@ -431,7 +550,7 @@
      * Returns true if {@link #getCodePhaseUncertaintyInChips()} is available, false otherwise.
      */
     public boolean hasCodePhaseUncertaintyInChips() {
-        return mHasCodePhaseUncertaintyInChips;
+        return isFlagSet(HAS_CODE_PHASE_UNCERTAINTY);
     }
 
     /**
@@ -448,7 +567,7 @@
      * Sets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
      */
     public void setCodePhaseUncertaintyInChips(double value) {
-        mHasCodePhaseUncertaintyInChips = true;
+        setFlag(HAS_CODE_PHASE_UNCERTAINTY);
         mCodePhaseUncertaintyInChips = value;
     }
 
@@ -456,7 +575,7 @@
      * Resets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
      */
     public void resetCodePhaseUncertaintyInChips() {
-        mHasCodePhaseUncertaintyInChips = false;
+        resetFlag(HAS_CODE_PHASE_UNCERTAINTY);
         mCodePhaseUncertaintyInChips = Double.NaN;
     }
 
@@ -464,7 +583,7 @@
      * Returns true if {@link #getCarrierFrequencyInHz()} is available, false otherwise.
      */
     public boolean hasCarrierFrequencyInHz() {
-        return mHasCarrierFrequencyInHz;
+        return isFlagSet(HAS_CARRIER_FREQUENCY);
     }
 
     /**
@@ -481,7 +600,7 @@
      * Sets the Carrier frequency (L1 or L2) in Hz.
      */
     public void setCarrierFrequencyInHz(float carrierFrequencyInHz) {
-        mHasCarrierFrequencyInHz = true;
+        setFlag(HAS_CARRIER_FREQUENCY);
         mCarrierFrequencyInHz = carrierFrequencyInHz;
     }
 
@@ -489,7 +608,7 @@
      * Resets the Carrier frequency (L1 or L2) in Hz.
      */
     public void resetCarrierFrequencyInHz() {
-        mHasCarrierFrequencyInHz = false;
+        resetFlag(HAS_CARRIER_FREQUENCY);
         mCarrierFrequencyInHz = Float.NaN;
     }
 
@@ -497,7 +616,7 @@
      * Returns true if {@link #getCarrierCycles()} is available, false otherwise.
      */
     public boolean hasCarrierCycles() {
-        return mHasCarrierCycles;
+        return isFlagSet(HAS_CARRIER_CYCLES);
     }
 
     /**
@@ -514,7 +633,7 @@
      * Sets the number of full carrier cycles between the satellite and the receiver.
      */
     public void setCarrierCycles(long value) {
-        mHasCarrierCycles = true;
+        setFlag(HAS_CARRIER_CYCLES);
         mCarrierCycles = value;
     }
 
@@ -522,7 +641,7 @@
      * Resets the number of full carrier cycles between the satellite and the receiver.
      */
     public void resetCarrierCycles() {
-        mHasCarrierCycles = false;
+        resetFlag(HAS_CARRIER_CYCLES);
         mCarrierCycles = Long.MIN_VALUE;
     }
 
@@ -530,7 +649,7 @@
      * Returns true if {@link #getCarrierPhase()} is available, false otherwise.
      */
     public boolean hasCarrierPhase() {
-        return mHasCarrierPhase;
+        return isFlagSet(HAS_CARRIER_PHASE);
     }
 
     /**
@@ -551,7 +670,7 @@
      * Sets the RF phase detected by the receiver.
      */
     public void setCarrierPhase(double value) {
-        mHasCarrierPhase = true;
+        setFlag(HAS_CARRIER_PHASE);
         mCarrierPhase = value;
     }
 
@@ -559,7 +678,7 @@
      * Resets the RF phase detected by the receiver.
      */
     public void resetCarrierPhase() {
-        mHasCarrierPhase = false;
+        resetFlag(HAS_CARRIER_PHASE);
         mCarrierPhase = Double.NaN;
     }
 
@@ -567,7 +686,7 @@
      * Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise.
      */
     public boolean hasCarrierPhaseUncertainty() {
-        return mHasCarrierPhaseUncertainty;
+        return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
     }
 
     /**
@@ -584,7 +703,7 @@
      * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
      */
     public void setCarrierPhaseUncertainty(double value) {
-        mHasCarrierPhaseUncertainty = true;
+        setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
         mCarrierPhaseUncertainty = value;
     }
 
@@ -592,21 +711,21 @@
      * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
      */
     public void resetCarrierPhaseUncertainty() {
-        mHasCarrierPhaseUncertainty = false;
+        resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
         mCarrierPhaseUncertainty = Double.NaN;
     }
 
     /**
      * Gets a value indicating the 'loss of lock' state of the event.
      */
-    public short getLossOfLock() {
+    public byte getLossOfLock() {
         return mLossOfLock;
     }
 
     /**
      * Sets the 'loss of lock' status.
      */
-    public void setLossOfLock(short value) {
+    public void setLossOfLock(byte value) {
         switch (value) {
             case LOSS_OF_LOCK_UNKNOWN:
             case LOSS_OF_LOCK_OK:
@@ -633,7 +752,7 @@
             case LOSS_OF_LOCK_CYCLE_SLIP:
                 return "CycleSlip";
             default:
-                return "Invalid";
+                return "<Invalid>";
         }
     }
 
@@ -641,7 +760,7 @@
      * Returns true if {@link #getBitNumber()} is available, false otherwise.
      */
     public boolean hasBitNumber() {
-        return mHasBitNumber;
+        return isFlagSet(HAS_BIT_NUMBER);
     }
 
     /**
@@ -657,7 +776,7 @@
      * Sets the bit number within the broadcast frame.
      */
     public void setBitNumber(short bitNumber) {
-        mHasBitNumber = true;
+        setFlag(HAS_BIT_NUMBER);
         mBitNumber = bitNumber;
     }
 
@@ -665,48 +784,48 @@
      * Resets the bit number within the broadcast frame.
      */
     public void resetBitNumber() {
-        mHasBitNumber = false;
+        resetFlag(HAS_BIT_NUMBER);
         mBitNumber = Short.MIN_VALUE;
     }
 
     /**
-     * Returns true if {@link #getTimeFromLastBitInNs()} is available, false otherwise.
+     * Returns true if {@link #getTimeFromLastBitInMs()} is available, false otherwise.
      */
-    public boolean hasTimeFromLastBitInNs() {
-        return mHasTimeFromLastBitInNs;
+    public boolean hasTimeFromLastBitInMs() {
+        return isFlagSet(HAS_TIME_FROM_LAST_BIT);
     }
 
     /**
-     * Gets the elapsed time since the last received bit in nanoseconds.
-     * Range: [0, 20000000].
+     * Gets the elapsed time since the last received bit in milliseconds.
+     * Range: [0, 20].
      *
-     * The value is only available if {@link #hasTimeFromLastBitInNs()} is true.
+     * The value is only available if {@link #hasTimeFromLastBitInMs()} is true.
      */
-    public long getTimeFromLastBitInNs() {
-        return mTimeFromLastBitInNs;
+    public short getTimeFromLastBitInMs() {
+        return mTimeFromLastBitInMs;
     }
 
     /**
-     * Sets the elapsed time since the last received bit in nanoseconds.
+     * Sets the elapsed time since the last received bit in milliseconds.
      */
-    public void setTimeFromLastBitInNs(long value) {
-        mHasTimeFromLastBitInNs = true;
-        mTimeFromLastBitInNs = value;
+    public void setTimeFromLastBitInMs(short value) {
+        setFlag(HAS_TIME_FROM_LAST_BIT);
+        mTimeFromLastBitInMs = value;
     }
 
     /**
-     * Resets the elapsed time since the last received bit in nanoseconds.
+     * Resets the elapsed time since the last received bit in milliseconds.
      */
-    public void resetTimeFromLastBitInNs() {
-        mHasTimeFromLastBitInNs = false;
-        mTimeFromLastBitInNs = Long.MIN_VALUE;
+    public void resetTimeFromLastBitInMs() {
+        resetFlag(HAS_TIME_FROM_LAST_BIT);
+        mTimeFromLastBitInMs = Short.MIN_VALUE;
     }
 
     /**
      * Returns true if {@link #getDopplerShiftInHz()} is available, false otherwise.
      */
     public boolean hasDopplerShiftInHz() {
-        return mHasDopplerShiftInHz;
+        return isFlagSet(HAS_DOPPLER_SHIFT);
     }
 
     /**
@@ -726,7 +845,7 @@
      * Sets the Doppler shift in Hz.
      */
     public void setDopplerShiftInHz(double value) {
-        mHasDopplerShiftInHz = true;
+        setFlag(HAS_DOPPLER_SHIFT);
         mDopplerShiftInHz = value;
     }
 
@@ -734,7 +853,7 @@
      * Resets the Doppler shift in Hz.
      */
     public void resetDopplerShiftInHz() {
-        mHasDopplerShiftInHz = false;
+        resetFlag(HAS_DOPPLER_SHIFT);
         mDopplerShiftInHz = Double.NaN;
     }
 
@@ -742,7 +861,7 @@
      * Returns true if {@link #getDopplerShiftUncertaintyInHz()} is available, false otherwise.
      */
     public boolean hasDopplerShiftUncertaintyInHz() {
-        return mHasDopplerShiftUncertaintyInHz;
+        return isFlagSet(HAS_DOPPLER_SHIFT_UNCERTAINTY);
     }
 
     /**
@@ -759,7 +878,7 @@
      * Sets the Doppler's shift uncertainty (1-Sigma) in Hz.
      */
     public void setDopplerShiftUncertaintyInHz(double value) {
-        mHasDopplerShiftUncertaintyInHz = true;
+        setFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
         mDopplerShiftUncertaintyInHz = value;
     }
 
@@ -767,21 +886,21 @@
      * Resets the Doppler's shift uncertainty (1-Sigma) in Hz.
      */
     public void resetDopplerShiftUncertaintyInHz() {
-        mHasDopplerShiftUncertaintyInHz = false;
+        resetFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
         mDopplerShiftUncertaintyInHz = Double.NaN;
     }
 
     /**
      * Gets a value indicating the 'multipath' state of the event.
      */
-    public short getMultipathIndicator() {
+    public byte getMultipathIndicator() {
         return mMultipathIndicator;
     }
 
     /**
      * Sets the 'multi-path' indicator.
      */
-    public void setMultipathIndicator(short value) {
+    public void setMultipathIndicator(byte value) {
         switch (value) {
             case MULTIPATH_INDICATOR_UNKNOWN:
             case MULTIPATH_INDICATOR_DETECTED:
@@ -806,9 +925,9 @@
             case MULTIPATH_INDICATOR_DETECTED:
                 return "Detected";
             case MULTIPATH_INDICATOR_NOT_USED:
-                return "NotDetected";
+                return "NotUsed";
             default:
-                return "Invalid";
+                return "<Invalid>";
         }
     }
 
@@ -816,7 +935,7 @@
      * Returns true if {@link #getSnrInDb()} is available, false otherwise.
      */
     public boolean hasSnrInDb() {
-        return mHasSnrInDb;
+        return isFlagSet(HAS_SNR);
     }
 
     /**
@@ -832,7 +951,7 @@
      * Sets the Signal-to-noise ratio (SNR) in dB.
      */
     public void setSnrInDb(double snrInDb) {
-        mHasSnrInDb = true;
+        setFlag(HAS_SNR);
         mSnrInDb = snrInDb;
     }
 
@@ -840,7 +959,7 @@
      * Resets the Signal-to-noise ratio (SNR) in dB.
      */
     public void resetSnrInDb() {
-        mHasSnrInDb = false;
+        resetFlag(HAS_SNR);
         mSnrInDb = Double.NaN;
     }
 
@@ -848,7 +967,7 @@
      * Returns true if {@link #getElevationInDeg()} is available, false otherwise.
      */
     public boolean hasElevationInDeg() {
-        return mHasElevationInDeg;
+        return isFlagSet(HAS_ELEVATION);
     }
 
     /**
@@ -866,7 +985,7 @@
      * Sets the Elevation in degrees.
      */
     public void setElevationInDeg(double elevationInDeg) {
-        mHasElevationInDeg = true;
+        setFlag(HAS_ELEVATION);
         mElevationInDeg = elevationInDeg;
     }
 
@@ -874,7 +993,7 @@
      * Resets the Elevation in degrees.
      */
     public void resetElevationInDeg() {
-        mHasElevationInDeg = false;
+        resetFlag(HAS_ELEVATION);
         mElevationInDeg = Double.NaN;
     }
 
@@ -882,7 +1001,7 @@
      * Returns true if {@link #getElevationUncertaintyInDeg()} is available, false otherwise.
      */
     public boolean hasElevationUncertaintyInDeg() {
-        return mHasElevationUncertaintyInDeg;
+        return isFlagSet(HAS_ELEVATION_UNCERTAINTY);
     }
 
     /**
@@ -901,7 +1020,7 @@
      * Sets the elevation's uncertainty (1-Sigma) in degrees.
      */
     public void setElevationUncertaintyInDeg(double value) {
-        mHasElevationUncertaintyInDeg = true;
+        setFlag(HAS_ELEVATION_UNCERTAINTY);
         mElevationUncertaintyInDeg = value;
     }
 
@@ -909,7 +1028,7 @@
      * Resets the elevation's uncertainty (1-Sigma) in degrees.
      */
     public void resetElevationUncertaintyInDeg() {
-        mHasElevationUncertaintyInDeg = false;
+        resetFlag(HAS_ELEVATION_UNCERTAINTY);
         mElevationUncertaintyInDeg = Double.NaN;
     }
 
@@ -917,7 +1036,7 @@
      * Returns true if {@link #getAzimuthInDeg()} is available, false otherwise.
      */
     public boolean hasAzimuthInDeg() {
-        return mHasAzimuthInDeg;
+        return isFlagSet(HAS_AZIMUTH);
     }
 
     /**
@@ -936,7 +1055,7 @@
      * Sets the Azimuth in degrees.
      */
     public void setAzimuthInDeg(double value) {
-        mHasAzimuthInDeg = true;
+        setFlag(HAS_AZIMUTH);
         mAzimuthInDeg = value;
     }
 
@@ -944,7 +1063,7 @@
      * Resets the Azimuth in degrees.
      */
     public void resetAzimuthInDeg() {
-        mHasAzimuthInDeg = false;
+        resetFlag(HAS_AZIMUTH);
         mAzimuthInDeg = Double.NaN;
     }
 
@@ -952,7 +1071,7 @@
      * Returns true if {@link #getAzimuthUncertaintyInDeg()} is available, false otherwise.
      */
     public boolean hasAzimuthUncertaintyInDeg() {
-        return mHasAzimuthUncertaintyInDeg;
+        return isFlagSet(HAS_AZIMUTH_UNCERTAINTY);
     }
 
     /**
@@ -971,7 +1090,7 @@
      * Sets the Azimuth's uncertainty (1-Sigma) in degrees.
      */
     public void setAzimuthUncertaintyInDeg(double value) {
-        mHasAzimuthUncertaintyInDeg = true;
+        setFlag(HAS_AZIMUTH_UNCERTAINTY);
         mAzimuthUncertaintyInDeg = value;
     }
 
@@ -979,7 +1098,7 @@
      * Resets the Azimuth's uncertainty (1-Sigma) in degrees.
      */
     public void resetAzimuthUncertaintyInDeg() {
-        mHasAzimuthUncertaintyInDeg = false;
+        resetFlag(HAS_AZIMUTH_UNCERTAINTY);
         mAzimuthUncertaintyInDeg = Double.NaN;
     }
 
@@ -989,7 +1108,7 @@
      *
      * @return A non-null value if the data is available, null otherwise.
      */
-    public Boolean isUsedInFix() {
+    public boolean isUsedInFix() {
         return mUsedInFix;
     }
 
@@ -1005,50 +1124,35 @@
         public GpsMeasurement createFromParcel(Parcel parcel) {
             GpsMeasurement gpsMeasurement = new GpsMeasurement();
 
+            gpsMeasurement.mFlags = parcel.readInt();
             gpsMeasurement.mPrn = parcel.readByte();
-            gpsMeasurement.mLocalTimeInNs = parcel.readLong();
+            gpsMeasurement.mTimeOffsetInNs = parcel.readDouble();
+            gpsMeasurement.mState = (short) parcel.readInt();
             gpsMeasurement.mReceivedGpsTowInNs = parcel.readLong();
             gpsMeasurement.mCn0InDbHz = parcel.readDouble();
             gpsMeasurement.mPseudorangeRateInMetersPerSec = parcel.readDouble();
             gpsMeasurement.mPseudorangeRateUncertaintyInMetersPerSec = parcel.readDouble();
+            gpsMeasurement.mAccumulatedDeltaRangeState = (short) parcel.readInt();
             gpsMeasurement.mAccumulatedDeltaRangeInMeters = parcel.readDouble();
             gpsMeasurement.mAccumulatedDeltaRangeUncertaintyInMeters = parcel.readDouble();
-
-            gpsMeasurement.mHasPseudorangeInMeters = parcel.readInt() != 0;
             gpsMeasurement.mPseudorangeInMeters = parcel.readDouble();
-            gpsMeasurement.mHasPseudorangeUncertaintyInMeters = parcel.readInt() != 0;
             gpsMeasurement.mPseudorangeUncertaintyInMeters = parcel.readDouble();
-            gpsMeasurement.mHasCodePhaseInChips = parcel.readInt() != 0;
             gpsMeasurement.mCodePhaseInChips = parcel.readDouble();
-            gpsMeasurement.mHasCodePhaseUncertaintyInChips = parcel.readInt() != 0;
             gpsMeasurement.mCodePhaseUncertaintyInChips = parcel.readDouble();
-            gpsMeasurement.mHasCarrierFrequencyInHz = parcel.readInt() != 0;
             gpsMeasurement.mCarrierFrequencyInHz = parcel.readFloat();
-            gpsMeasurement.mHasCarrierCycles = parcel.readInt() != 0;
             gpsMeasurement.mCarrierCycles = parcel.readLong();
-            gpsMeasurement.mHasCarrierPhase = parcel.readInt() != 0;
             gpsMeasurement.mCarrierPhase = parcel.readDouble();
-            gpsMeasurement.mHasCarrierPhaseUncertainty = parcel.readInt() != 0;
             gpsMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
-            gpsMeasurement.mLossOfLock = (short) parcel.readInt();
-            gpsMeasurement.mHasBitNumber = parcel.readInt() != 0;
+            gpsMeasurement.mLossOfLock = parcel.readByte();
             gpsMeasurement.mBitNumber = (short) parcel.readInt();
-            gpsMeasurement.mHasTimeFromLastBitInNs = parcel.readInt() != 0;
-            gpsMeasurement.mTimeFromLastBitInNs = parcel.readLong();
-            gpsMeasurement.mHasDopplerShiftInHz = parcel.readInt() != 0;
+            gpsMeasurement.mTimeFromLastBitInMs = (short) parcel.readInt();
             gpsMeasurement.mDopplerShiftInHz = parcel.readDouble();
-            gpsMeasurement.mHasDopplerShiftUncertaintyInHz = parcel.readInt() != 0;
             gpsMeasurement.mDopplerShiftUncertaintyInHz = parcel.readDouble();
-            gpsMeasurement.mMultipathIndicator = (short) parcel.readInt();
-            gpsMeasurement.mHasSnrInDb = parcel.readInt() != 0;
+            gpsMeasurement.mMultipathIndicator = parcel.readByte();
             gpsMeasurement.mSnrInDb = parcel.readDouble();
-            gpsMeasurement.mHasElevationInDeg = parcel.readInt() != 0;
             gpsMeasurement.mElevationInDeg = parcel.readDouble();
-            gpsMeasurement.mHasElevationUncertaintyInDeg = parcel.readInt() != 0;
             gpsMeasurement.mElevationUncertaintyInDeg = parcel.readDouble();
-            gpsMeasurement.mHasAzimuthInDeg = parcel.readInt() != 0;
             gpsMeasurement.mAzimuthInDeg = parcel.readDouble();
-            gpsMeasurement.mHasAzimuthUncertaintyInDeg = parcel.readInt() != 0;
             gpsMeasurement.mAzimuthUncertaintyInDeg = parcel.readDouble();
             gpsMeasurement.mUsedInFix = parcel.readInt() != 0;
 
@@ -1062,50 +1166,35 @@
     };
 
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mFlags);
         parcel.writeByte(mPrn);
-        parcel.writeLong(mLocalTimeInNs);
+        parcel.writeDouble(mTimeOffsetInNs);
+        parcel.writeInt(mState);
         parcel.writeLong(mReceivedGpsTowInNs);
         parcel.writeDouble(mCn0InDbHz);
         parcel.writeDouble(mPseudorangeRateInMetersPerSec);
         parcel.writeDouble(mPseudorangeRateUncertaintyInMetersPerSec);
+        parcel.writeInt(mAccumulatedDeltaRangeState);
         parcel.writeDouble(mAccumulatedDeltaRangeInMeters);
         parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyInMeters);
-
-        parcel.writeInt(mHasPseudorangeInMeters ? 1 : 0);
         parcel.writeDouble(mPseudorangeInMeters);
-        parcel.writeInt(mHasPseudorangeUncertaintyInMeters ? 1 : 0);
         parcel.writeDouble(mPseudorangeUncertaintyInMeters);
-        parcel.writeInt(mHasCodePhaseInChips ? 1 : 0);
         parcel.writeDouble(mCodePhaseInChips);
-        parcel.writeInt(mHasCodePhaseUncertaintyInChips ? 1 : 0);
         parcel.writeDouble(mCodePhaseUncertaintyInChips);
-        parcel.writeInt(mHasCarrierFrequencyInHz ? 1 : 0);
         parcel.writeFloat(mCarrierFrequencyInHz);
-        parcel.writeInt(mHasCarrierCycles ? 1 : 0);
         parcel.writeLong(mCarrierCycles);
-        parcel.writeInt(mHasCarrierPhase ? 1 : 0);
         parcel.writeDouble(mCarrierPhase);
-        parcel.writeInt(mHasCarrierPhaseUncertainty ? 1 : 0);
         parcel.writeDouble(mCarrierPhaseUncertainty);
-        parcel.writeInt(mLossOfLock);
-        parcel.writeInt(mHasBitNumber ? 1 : 0);
+        parcel.writeByte(mLossOfLock);
         parcel.writeInt(mBitNumber);
-        parcel.writeInt(mHasTimeFromLastBitInNs ? 1 : 0);
-        parcel.writeLong(mTimeFromLastBitInNs);
-        parcel.writeInt(mHasDopplerShiftInHz ? 1 : 0);
+        parcel.writeInt(mTimeFromLastBitInMs);
         parcel.writeDouble(mDopplerShiftInHz);
-        parcel.writeInt(mHasDopplerShiftUncertaintyInHz ? 1 : 0);
         parcel.writeDouble(mDopplerShiftUncertaintyInHz);
-        parcel.writeInt(mMultipathIndicator);
-        parcel.writeInt(mHasSnrInDb ? 1 : 0);
+        parcel.writeByte(mMultipathIndicator);
         parcel.writeDouble(mSnrInDb);
-        parcel.writeInt(mHasElevationInDeg ? 1 : 0);
         parcel.writeDouble(mElevationInDeg);
-        parcel.writeInt(mHasElevationUncertaintyInDeg ? 1 : 0);
         parcel.writeDouble(mElevationUncertaintyInDeg);
-        parcel.writeInt(mHasAzimuthInDeg ? 1 : 0);
         parcel.writeDouble(mAzimuthInDeg);
-        parcel.writeInt(mHasAzimuthUncertaintyInDeg ? 1 : 0);
         parcel.writeDouble(mAzimuthUncertaintyInDeg);
         parcel.writeInt(mUsedInFix ? 1 : 0);
     }
@@ -1123,7 +1212,9 @@
 
         builder.append(String.format(format, "Prn", mPrn));
 
-        builder.append(String.format(format, "LocalTimeInNs", mLocalTimeInNs));
+        builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
+
+        builder.append(String.format(format, "State", getStateString()));
 
         builder.append(String.format(format, "ReceivedGpsTowInNs", mReceivedGpsTowInNs));
 
@@ -1137,86 +1228,136 @@
                 mPseudorangeRateUncertaintyInMetersPerSec));
 
         builder.append(String.format(
+                format,
+                "AccumulatedDeltaRangeState",
+                getAccumulatedDeltaRangeStateString()));
+
+        builder.append(String.format(
                 formatWithUncertainty,
                 "AccumulatedDeltaRangeInMeters",
                 mAccumulatedDeltaRangeInMeters,
                 "AccumulatedDeltaRangeUncertaintyInMeters",
                 mAccumulatedDeltaRangeUncertaintyInMeters));
 
-
         builder.append(String.format(
                 formatWithUncertainty,
                 "PseudorangeInMeters",
-                mHasPseudorangeInMeters ? mPseudorangeInMeters : null,
+                hasPseudorangeInMeters() ? mPseudorangeInMeters : null,
                 "PseudorangeUncertaintyInMeters",
-                mHasPseudorangeUncertaintyInMeters ? mPseudorangeUncertaintyInMeters : null));
+                hasPseudorangeUncertaintyInMeters() ? mPseudorangeUncertaintyInMeters : null));
 
         builder.append(String.format(
                 formatWithUncertainty,
                 "CodePhaseInChips",
-                mHasCodePhaseInChips ? mCodePhaseInChips : null,
+                hasCodePhaseInChips() ? mCodePhaseInChips : null,
                 "CodePhaseUncertaintyInChips",
-                mHasCodePhaseUncertaintyInChips ? mCodePhaseUncertaintyInChips : null));
+                hasCodePhaseUncertaintyInChips() ? mCodePhaseUncertaintyInChips : null));
 
         builder.append(String.format(
                 format,
                 "CarrierFrequencyInHz",
-                mHasCarrierFrequencyInHz ? mCarrierFrequencyInHz : null));
+                hasCarrierFrequencyInHz() ? mCarrierFrequencyInHz : null));
 
         builder.append(String.format(
                 format,
                 "CarrierCycles",
-                mHasCarrierCycles ? mCarrierCycles : null));
+                hasCarrierCycles() ? mCarrierCycles : null));
 
         builder.append(String.format(
                 formatWithUncertainty,
                 "CarrierPhase",
-                mHasCarrierPhase ? mCarrierPhase : null,
+                hasCarrierPhase() ? mCarrierPhase : null,
                 "CarrierPhaseUncertainty",
-                mHasCarrierPhaseUncertainty ? mCarrierPhaseUncertainty : null));
+                hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
 
         builder.append(String.format(format, "LossOfLock", getLossOfLockString()));
 
         builder.append(String.format(
                 format,
                 "BitNumber",
-                mHasBitNumber ? mBitNumber : null));
+                hasBitNumber() ? mBitNumber : null));
 
         builder.append(String.format(
                 format,
-                "TimeFromLastBitInNs",
-                mHasTimeFromLastBitInNs ? mTimeFromLastBitInNs : null));
+                "TimeFromLastBitInMs",
+                hasTimeFromLastBitInMs() ? mTimeFromLastBitInMs : null));
 
         builder.append(String.format(
                 formatWithUncertainty,
                 "DopplerShiftInHz",
-                mHasDopplerShiftInHz ? mDopplerShiftInHz : null,
+                hasDopplerShiftInHz() ? mDopplerShiftInHz : null,
                 "DopplerShiftUncertaintyInHz",
-                mHasDopplerShiftUncertaintyInHz ? mDopplerShiftUncertaintyInHz : null));
+                hasDopplerShiftUncertaintyInHz() ? mDopplerShiftUncertaintyInHz : null));
 
         builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
 
         builder.append(String.format(
                 format,
                 "SnrInDb",
-                mHasSnrInDb ? mSnrInDb : null));
+                hasSnrInDb() ? mSnrInDb : null));
 
         builder.append(String.format(
                 formatWithUncertainty,
                 "ElevationInDeg",
-                mHasElevationInDeg ? mElevationInDeg : null,
+                hasElevationInDeg() ? mElevationInDeg : null,
                 "ElevationUncertaintyInDeg",
-                mHasElevationUncertaintyInDeg ? mElevationUncertaintyInDeg : null));
+                hasElevationUncertaintyInDeg() ? mElevationUncertaintyInDeg : null));
 
         builder.append(String.format(
                 formatWithUncertainty,
                 "AzimuthInDeg",
-                mHasAzimuthInDeg ? mAzimuthInDeg : null,
+                hasAzimuthInDeg() ? mAzimuthInDeg : null,
                 "AzimuthUncertaintyInDeg",
-                mHasAzimuthUncertaintyInDeg ? mAzimuthUncertaintyInDeg : null));
+                hasAzimuthUncertaintyInDeg() ? mAzimuthUncertaintyInDeg : null));
 
         builder.append(String.format(format, "UsedInFix", mUsedInFix));
 
         return builder.toString();
     }
+
+    private void initialize() {
+        mFlags = HAS_NO_FLAGS;
+        setPrn(Byte.MIN_VALUE);
+        setTimeOffsetInNs(Long.MIN_VALUE);
+        setState(STATE_UNKNOWN);
+        setReceivedGpsTowInNs(Long.MIN_VALUE);
+        setCn0InDbHz(Double.MIN_VALUE);
+        setPseudorangeRateInMetersPerSec(Double.MIN_VALUE);
+        setPseudorangeRateUncertaintyInMetersPerSec(Double.MIN_VALUE);
+        setAccumulatedDeltaRangeState(ADR_STATE_UNKNOWN);
+        setAccumulatedDeltaRangeInMeters(Double.MIN_VALUE);
+        setAccumulatedDeltaRangeUncertaintyInMeters(Double.MIN_VALUE);
+        resetPseudorangeInMeters();
+        resetPseudorangeUncertaintyInMeters();
+        resetCodePhaseInChips();
+        resetCodePhaseUncertaintyInChips();
+        resetCarrierFrequencyInHz();
+        resetCarrierCycles();
+        resetCarrierPhase();
+        resetCarrierPhaseUncertainty();
+        setLossOfLock(LOSS_OF_LOCK_UNKNOWN);
+        resetBitNumber();
+        resetTimeFromLastBitInMs();
+        resetDopplerShiftInHz();
+        resetDopplerShiftUncertaintyInHz();
+        setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
+        resetSnrInDb();
+        resetElevationInDeg();
+        resetElevationUncertaintyInDeg();
+        resetAzimuthInDeg();
+        resetAzimuthUncertaintyInDeg();
+        setUsedInFix(false);
+    }
+
+    private void setFlag(int flag) {
+        mFlags |= flag;
+    }
+
+    private void resetFlag(int flag) {
+        mFlags &= ~flag;
+    }
+
+    private boolean isFlagSet(int flag) {
+        return (mFlags & flag) == flag;
+    }
 }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index a1a7af9..e4102e90 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -3500,7 +3500,13 @@
 
                 loadTouchSoundAssets();
 
-                mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
+                mSoundPool = new SoundPool.Builder()
+                        .setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
+                        .setAudioAttributes(new AudioAttributes.Builder()
+                            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                            .build())
+                        .build();
                 mSoundPoolCallBack = null;
                 mSoundPoolListenerThread = new SoundPoolListenerThread();
                 mSoundPoolListenerThread.start();
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 16a2235..b5cebe3 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -65,6 +65,13 @@
  * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr>
  * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>
  * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr>
+ * <tr><td>{@link #KEY_AAC_SBR_MODE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired SBR mode.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_TARGET_REFERENCE_LEVEL}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the target reference level.</td></tr>
+ * <tr><td>{@link #KEY_AAC_ENCODED_TARGET_LEVEL}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the target reference level used at encoder.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_BOOST_FACTOR}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the DRC boost factor.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_ATTENUATION_FACTOR}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the DRC attenuation factor.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_HEAVY_COMPRESSION}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies whether to use heavy compression.</td></tr>
+ * <tr><td>{@link #KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the maximum number of channels the decoder outputs.</td></tr>
  * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>optional, a mask of audio channel assignments</td></tr>
  * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>
  * </table>
@@ -261,6 +268,110 @@
     public static final String KEY_AAC_PROFILE = "aac-profile";
 
     /**
+     * A key describing the AAC SBR mode to be used (AAC audio formats only).
+     * The associated value is an integer and can be set to following values:
+     * <ul>
+     * <li>0 - no SBR should be applied</li>
+     * <li>1 - single rate SBR</li>
+     * <li>2 - double rate SBR</li>
+     * </ul>
+     * Note: If this key is not defined the default SRB mode for the desired AAC profile will
+     * be used.
+     * <p>This key is only used during encoding.
+     */
+    public static final String KEY_AAC_SBR_MODE = "aac-sbr-mode";
+
+    /**
+     * A key describing the maximum number of channels that can be output by the AAC decoder.
+     * By default, the decoder will output the same number of channels as present in the encoded
+     * stream, if supported. Set this value to limit the number of output channels, and use
+     * the downmix information in the stream, if available.
+     * <p>Values larger than the number of channels in the content to decode are ignored.
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count";
+
+    /**
+     * A key describing a gain to be applied so that the output loudness matches the
+     * Target Reference Level. This is typically used to normalize loudness across program items.
+     * The gain is derived as the difference between the Target Reference Level and the
+     * Program Reference Level. The latter can be given in the bitstream and indicates the actual
+     * loudness value of the program item.
+     * <p>The value is given as an integer value between
+     * 0 and 127, and is calculated as -0.25 * Target Reference Level in dBFS.
+     * Therefore, it represents the range of Full Scale (0 dBFS) to -31.75 dBFS.
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level";
+
+    /**
+     * A key describing the target reference level that was assumed at the encoder for
+     * calculation of attenuation gains for clipping prevention. This information can be provided
+     * if it is known, otherwise a worst-case assumption is used.
+     * <p>The value is given as an integer value between
+     * 0 and 127, and is calculated as -0.25 * Target Reference Level in dBFS.
+     * Therefore, it represents the range of Full Scale (0 dBFS) to -31.75 dBFS.
+     * The default value is the worst-case assumption of 127.
+     * <p>The value is ignored when heavy compression is used (see
+     * {@link #KEY_AAC_DRC_HEAVY_COMPRESSION}).
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level";
+
+    /**
+     * A key describing the boost factor allowing to adapt the dynamics of the output to the
+     * actual listening requirements. This relies on DRC gain sequences that can be transmitted in
+     * the encoded bitstream to be able to reduce the dynamics of the output signal upon request.
+     * This factor enables the user to select how much of the gains are applied.
+     * <p>Positive gains (boost) and negative gains (attenuation, see
+     * {@link #KEY_AAC_DRC_ATTENUATION_FACTOR}) can be controlled separately for a better match
+     * to different use-cases.
+     * <p>Typically, attenuation gains are sent for loud signal segments, and boost gains are sent
+     * for soft signal segments. If the output is listened to in a noisy environment, for example,
+     * the boost factor is used to enable the positive gains, i.e. to amplify soft signal segments
+     * beyond the noise floor. But for listening late at night, the attenuation
+     * factor is used to enable the negative gains, to prevent loud signal from surprising
+     * the listener. In applications which generally need a low dynamic range, both the boost factor
+     * and the attenuation factor are used in order to enable all DRC gains.
+     * <p>In order to prevent clipping, it is also recommended to apply the attenuation factors
+     * in case of a downmix and/or loudness normalization to high target reference levels.
+     * <p>Both the boost and the attenuation factor parameters are given as integer values
+     * between 0 and 127, representing the range of the factor of 0 (i.e. don't apply)
+     * to 1 (i.e. fully apply boost/attenuation factors respectively).
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
+
+    /**
+     * A key describing the attenuation factor allowing to adapt the dynamics of the output to the
+     * actual listening requirements.
+     * See {@link #KEY_AAC_DRC_BOOST_FACTOR} for a description of the role of this attenuation
+     * factor and the value range.
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
+
+    /**
+     * A key describing the selection of the heavy compression profile for DRC.
+     * Two separate DRC gain sequences can be transmitted in one bitstream: MPEG-4 DRC light
+     * compression, and DVB-specific heavy compression. When selecting the application of the heavy
+     * compression, one of the sequences is selected:
+     * <ul>
+     * <li>0 enables light compression,</li>
+     * <li>1 enables heavy compression instead.
+     * </ul>
+     * Note that only light compression offers the features of scaling of DRC gains
+     * (see {@link #KEY_AAC_DRC_BOOST_FACTOR} and {@link #KEY_AAC_DRC_ATTENUATION_FACTOR} for the
+     * boost and attenuation factors, and frequency-selective (multiband) DRC.
+     * Light compression usually contains clipping prevention for stereo downmixing while heavy
+     * compression, if additionally provided in the bitstream, is usually stronger, and contains
+     * clipping prevention for stereo and mono downmixing.
+     * <p>The default is light compression.
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
+
+    /**
      * A key describing the FLAC compression level to be used (FLAC audio format only).
      * The associated value is an integer ranging from 0 (fastest, least compression)
      * to 8 (slowest, most compression).
diff --git a/media/java/android/media/MediaMetadataEditor.java b/media/java/android/media/MediaMetadataEditor.java
index ca44e9d..566b93f 100644
--- a/media/java/android/media/MediaMetadataEditor.java
+++ b/media/java/android/media/MediaMetadataEditor.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.graphics.Bitmap;
+import android.media.session.MediaSession;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.util.Log;
@@ -26,8 +27,10 @@
  * An abstract class for editing and storing metadata that can be published by
  * {@link RemoteControlClient}. See the {@link RemoteControlClient#editMetadata(boolean)}
  * method to instantiate a {@link RemoteControlClient.MetadataEditor} object.
+ *
+ * @deprecated Use {@link MediaMetadata} instead together with {@link MediaSession}.
  */
-public abstract class MediaMetadataEditor {
+@Deprecated public abstract class MediaMetadataEditor {
 
     private final static String TAG = "MediaMetadataEditor";
     /**
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index eb543b4..54ad60e 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -40,8 +40,9 @@
    the right get* method to fetch its value.
    
    @hide
+   @deprecated Use {@link MediaMetadata}.
  */
-public class Metadata
+@Deprecated public class Metadata
 {
     // The metadata are keyed using integers rather than more heavy
     // weight strings. We considered using Bundle to ship the metadata
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 740a9d3..0235871 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -63,8 +63,10 @@
  * // create and register the remote control client
  * RemoteControlClient myRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
  * myAudioManager.registerRemoteControlClient(myRemoteControlClient);</pre>
+ *
+ * @deprecated Use {@link MediaSession} instead.
  */
-public class RemoteControlClient
+@Deprecated public class RemoteControlClient
 {
     private final static String TAG = "RemoteControlClient";
     private final static boolean DEBUG = false;
@@ -425,8 +427,10 @@
      * has been set, use {@link #apply()} to make it the new metadata that should be displayed
      * for the associated client. Once the metadata has been "applied", you cannot reuse this
      * instance of the MetadataEditor.
+     *
+     * @deprecated Use {@link MediaMetadata} and {@link MediaSession} instead.
      */
-    public class MetadataEditor extends MediaMetadataEditor {
+    @Deprecated public class MetadataEditor extends MediaMetadataEditor {
 
         // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
         private MetadataEditor() { }
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 9ea3f26..966fbb0 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -57,8 +57,10 @@
  * <p>
  * Registration requires the {@link OnClientUpdateListener} listener to be one of the enabled
  * notification listeners (see {@link android.service.notification.NotificationListenerService}).
+ *
+ * @deprecated Use {@link MediaController} instead.
  */
-public final class RemoteController
+@Deprecated public final class RemoteController
 {
     private final static int MAX_BITMAP_DIMENSION = 512;
     private final static int TRANSPORT_UNKNOWN = 0;
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 44c193f..9b9c767 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -126,12 +126,71 @@
      * @param srcQuality the sample-rate converter quality. Currently has no
      *                   effect. Use 0 for the default.
      * @return a SoundPool object, or null if creation failed
+     * @deprecated use {@link SoundPool.Builder} instead to create and configure a
+     *     SoundPool instance
      */
     public SoundPool(int maxStreams, int streamType, int srcQuality) {
+        this(maxStreams,
+                new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
+    }
+
+    private SoundPool(int maxStreams, AudioAttributes attributes) {
         if (SystemProperties.getBoolean("config.disable_media", false)) {
             mImpl = new SoundPoolStub();
         } else {
-            mImpl = new SoundPoolImpl(this, maxStreams, streamType, srcQuality);
+            mImpl = new SoundPoolImpl(this, maxStreams, attributes);
+        }
+    }
+
+    /**
+     * Builder class for {@link SoundPool} objects.
+     */
+    public static class Builder {
+        private int mMaxStreams = 1;
+        private AudioAttributes mAudioAttributes;
+
+        /**
+         * Constructs a new Builder with the defaults format values.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the maximum of number of simultaneous streams that can be played simultaneously.
+         * @param maxStreams a value equal to 1 or greater.
+         * @return the same Builder instance
+         * @throws IllegalArgumentException
+         */
+        public Builder setMaxStreams(int maxStreams) throws IllegalArgumentException {
+            if (maxStreams <= 0) {
+                throw new IllegalArgumentException(
+                        "Strictly positive value required for the maximum number of streams");
+            }
+            mMaxStreams = maxStreams;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AudioAttributes}. For examples, game applications will use attributes
+         * built with usage information set to {@link AudioAttributes#USAGE_GAME}.
+         * @param attributes a non-null
+         * @return
+         */
+        public Builder setAudioAttributes(AudioAttributes attributes)
+                throws IllegalArgumentException {
+            if (attributes == null) {
+                throw new IllegalArgumentException("Invalid null AudioAttributes");
+            }
+            mAudioAttributes = attributes;
+            return this;
+        }
+
+        public SoundPool build() {
+            if (mAudioAttributes == null) {
+                mAudioAttributes = new AudioAttributes.Builder()
+                        .setUsage(AudioAttributes.USAGE_MEDIA).build();
+            }
+            return new SoundPool(mMaxStreams, mAudioAttributes);
         }
     }
 
@@ -457,7 +516,7 @@
         private SoundPool mProxy;
 
         private final Object mLock;
-        private final int mStreamType;
+        private final AudioAttributes mAttributes;
         private final IAppOpsService mAppOps;
 
         // SoundPool messages
@@ -465,15 +524,15 @@
         // must match SoundPool.h
         private static final int SAMPLE_LOADED = 1;
 
-        public SoundPoolImpl(SoundPool proxy, int maxStreams, int streamType, int srcQuality) {
+        public SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr) {
 
             // do native setup
-            if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
+            if (native_setup(new WeakReference(this), maxStreams, attr) != 0) {
                 throw new RuntimeException("Native setup failed");
             }
             mLock = new Object();
             mProxy = proxy;
-            mStreamType = streamType;
+            mAttributes = attr;
             IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
             mAppOps = IAppOpsService.Stub.asInterface(b);
         }
@@ -548,9 +607,9 @@
 
         private boolean isRestricted() {
             try {
-                final int usage = AudioAttributes.usageForLegacyStreamType(mStreamType);
                 final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
-                        usage, Process.myUid(), ActivityThread.currentPackageName());
+                        mAttributes.getUsage(),
+                        Process.myUid(), ActivityThread.currentPackageName());
                 return mode != AppOpsManager.MODE_ALLOWED;
             } catch (RemoteException e) {
                 return false;
@@ -648,7 +707,8 @@
 
         public native final void release();
 
-        private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
+        private native final int native_setup(Object weakRef, int maxStreams,
+                Object/*AudioAttributes*/ attributes);
 
         protected void finalize() { release(); }
     }
diff --git a/media/java/android/media/browse/IMediaBrowserService.aidl b/media/java/android/media/browse/IMediaBrowserService.aidl
new file mode 100644
index 0000000..4b2cb9d
--- /dev/null
+++ b/media/java/android/media/browse/IMediaBrowserService.aidl
@@ -0,0 +1,20 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+
+package android.media.browse;
+
+import android.media.browse.IMediaBrowserServiceCallbacks;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Media API allows clients to browse through hierarchy of a user’s media collection,
+ * playback a specific media entry and interact with the now playing queue.
+ * @hide
+ */
+oneway interface IMediaBrowserService {
+    void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks);
+    void disconnect(IMediaBrowserServiceCallbacks callbacks);
+
+    void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
+    void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
+}
\ No newline at end of file
diff --git a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl b/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
new file mode 100644
index 0000000..ead7624
--- /dev/null
+++ b/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
@@ -0,0 +1,24 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+
+package android.media.browse;
+
+import android.content.pm.ParceledListSlice;
+import android.media.session.MediaSession;
+import android.net.Uri;
+
+/**
+ * Media API allows clients to browse through hierarchy of a user’s media collection,
+ * playback a specific media entry and interact with the now playing queue.
+ * @hide
+ */
+oneway interface IMediaBrowserServiceCallbacks {
+    /**
+     * Invoked when the connected has been established.
+     * @param root The root Uri for browsing.
+     * @param session The {@link MediaSession.Token media session token} that can be used to control
+     * the playback of the media app.
+     */
+    void onConnect(in Uri root, in MediaSession.Token session);
+    void onConnectFailed();
+    void onLoadChildren(in Uri uri, in ParceledListSlice list);
+}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
new file mode 100644
index 0000000..beec5f9
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -0,0 +1,704 @@
+/*
+ * 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.browse;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Browses media content offered by a link MediaBrowserService.
+ * <p>
+ * This object is not thread-safe. All calls should happen on the thread on which the browser
+ * was constructed.
+ * </p>
+ */
+public final class MediaBrowser {
+    private static final String TAG = "MediaBrowser";
+    private static final boolean DBG = false;
+
+    private static final int CONNECT_STATE_DISCONNECTED = 0;
+    private static final int CONNECT_STATE_CONNECTING = 1;
+    private static final int CONNECT_STATE_CONNECTED = 2;
+    private static final int CONNECT_STATE_SUSPENDED = 3;
+
+    private final Context mContext;
+    private final ComponentName mServiceComponent;
+    private final ConnectionCallback mCallback;
+    private final Bundle mRootHints;
+    private final Handler mHandler = new Handler();
+    private final ArrayMap<Uri,Subscription> mSubscriptions =
+            new ArrayMap<Uri, MediaBrowser.Subscription>();
+
+    private int mState = CONNECT_STATE_DISCONNECTED;
+    private MediaServiceConnection mServiceConnection;
+    private IMediaBrowserService mServiceBinder;
+    private IMediaBrowserServiceCallbacks mServiceCallbacks;
+    private Uri mRootUri;
+    private MediaSession.Token mMediaSessionToken;
+
+    /**
+     * Creates a media browser for the specified media browse service.
+     *
+     * @param context The context.
+     * @param serviceComponent The component name of the media browse service.
+     * @param callback The connection callback.
+     * @param rootHints An optional bundle of service-specific arguments to send
+     * to the media browse service when connecting and retrieving the root uri
+     * for browsing, or null if none.  The contents of this bundle may affect
+     * the information returned when browsing.
+     */
+    public MediaBrowser(Context context, ComponentName serviceComponent,
+            ConnectionCallback callback, Bundle rootHints) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (serviceComponent == null) {
+            throw new IllegalArgumentException("service component must not be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("connection callback must not be null");
+        }
+        mContext = context;
+        mServiceComponent = serviceComponent;
+        mCallback = callback;
+        mRootHints = rootHints;
+    }
+
+    /**
+     * Connects to the media browse service.
+     * <p>
+     * The connection callback specified in the constructor will be invoked
+     * when the connection completes or fails.
+     * </p>
+     */
+    public void connect() {
+        if (mState != CONNECT_STATE_DISCONNECTED) {
+            throw new IllegalStateException("connect() called while not disconnected (state="
+                    + getStateLabel(mState) + ")");
+        }
+        // TODO: remove this extra check.
+        if (DBG) {
+            if (mServiceConnection != null) {
+                throw new RuntimeException("mServiceConnection should be null. Instead it is "
+                        + mServiceConnection);
+            }
+        }
+        if (mServiceBinder != null) {
+            throw new RuntimeException("mServiceBinder should be null. Instead it is "
+                    + mServiceBinder);
+        }
+        if (mServiceCallbacks != null) {
+            throw new RuntimeException("mServiceCallbacks should be null. Instead it is "
+                    + mServiceCallbacks);
+        }
+
+        mState = CONNECT_STATE_CONNECTING;
+
+        final Intent intent = new Intent(MediaBrowserService.SERVICE_ACTION);
+        intent.setComponent(mServiceComponent);
+
+        final ServiceConnection thisConnection = mServiceConnection = new MediaServiceConnection();
+
+        try {
+            mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed binding to service " + mServiceComponent);
+
+            // Tell them that it didn't work.  We are already on the main thread,
+            // but we don't want to do callbacks inside of connect().  So post it,
+            // and then check that we are on the same ServiceConnection.  We know
+            // we won't also get an onServiceConnected or onServiceDisconnected,
+            // so we won't be doing double callbacks.
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Ensure that nobody else came in or tried to connect again.
+                        if (thisConnection == mServiceConnection) {
+                            forceCloseConnection();
+                            mCallback.onConnectionFailed();
+                        }
+                    }
+                });
+        }
+
+        if (DBG) {
+            Log.d(TAG, "connect...");
+            dump();
+        }
+    }
+
+    /**
+     * Disconnects from the media browse service.
+     * @more
+     * After this, no more callbacks will be received.
+     */
+    public void disconnect() {
+        // It's ok to call this any state, because allowing this lets apps not have
+        // to check isConnected() unnecessarily.  They won't appreciate the extra
+        // assertions for this.  We do everything we can here to go back to a sane state.
+        if (mServiceCallbacks != null) {
+            try {
+                mServiceBinder.disconnect(mServiceCallbacks);
+            } catch (RemoteException ex) {
+                // We are disconnecting anyway.  Log, just for posterity but it's not
+                // a big problem.
+                Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+            }
+        }
+        forceCloseConnection();
+
+        if (DBG) {
+            Log.d(TAG, "disconnect...");
+            dump();
+        }
+    }
+
+    /**
+     * Null out the variables and unbind from the service.  This doesn't include
+     * calling disconnect on the service, because we only try to do that in the
+     * clean shutdown cases.
+     * <p>
+     * Everywhere that calls this EXCEPT for disconnect() should follow it with
+     * a call to mCallback.onConnectionFailed().  Disconnect doesn't do that callback
+     * for a clean shutdown, but everywhere else is a dirty shutdown and should
+     * notify the app.
+     */
+    private void forceCloseConnection() {
+        if (mServiceConnection != null) {
+            mContext.unbindService(mServiceConnection);
+        }
+        mState = CONNECT_STATE_DISCONNECTED;
+        mServiceConnection = null;
+        mServiceBinder = null;
+        mServiceCallbacks = null;
+        mRootUri = null;
+        mMediaSessionToken = null;
+    }
+
+    /**
+     * Returns whether the browser is connected to the service.
+     */
+    public boolean isConnected() {
+        return mState == CONNECT_STATE_CONNECTED;
+    }
+
+    /**
+     * Gets the root Uri.
+     * <p>
+     * Note that the root uri may become invalid or change when when the
+     * browser is disconnected.
+     * </p>
+     *
+     * @throws IllegalStateException if not connected.
+      */
+    public @NonNull Uri getRoot() {
+        if (mState != CONNECT_STATE_CONNECTED) {
+            throw new IllegalStateException("getSessionToken() called while not connected (state="
+                    + getStateLabel(mState) + ")");
+        }
+        return mRootUri;
+    }
+
+    /**
+     * Gets the media session token associated with the media browser.
+     * <p>
+     * Note that the session token may become invalid or change when when the
+     * browser is disconnected.
+     * </p>
+     *
+     * @return The session token for the browser, never null.
+     *
+     * @throws IllegalStateException if not connected.
+     */
+     public @NonNull MediaSession.Token getSessionToken() {
+        if (mState != CONNECT_STATE_CONNECTED) {
+            throw new IllegalStateException("getSessionToken() called while not connected (state="
+                    + mState + ")");
+        }
+        return mMediaSessionToken;
+    }
+
+    /**
+     * Queries for information about the media items that are contained within
+     * the specified Uri and subscribes to receive updates when they change.
+     * <p>
+     * The list of subscriptions is maintained even when not connected and is
+     * restored after reconnection.  It is ok to subscribe while not connected
+     * but the results will not be returned until the connection completes.
+     * </p><p>
+     * If the uri is already subscribed with a different callback then the new
+     * callback will replace the previous one.
+     * </p>
+     *
+     * @param parentUri The uri of the parent media item whose list of children
+     * will be subscribed.
+     * @param callback The callback to receive the list of children.
+     */
+    public void subscribe(@NonNull Uri parentUri, @NonNull SubscriptionCallback callback) {
+        // Check arguments.
+        if (parentUri == null) {
+            throw new IllegalArgumentException("parentUri is null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback is null");
+        }
+
+        // Update or create the subscription.
+        Subscription sub = mSubscriptions.get(parentUri);
+        boolean newSubscription = sub == null;
+        if (newSubscription) {
+            sub = new Subscription(parentUri);
+            mSubscriptions.put(parentUri, sub);
+        }
+        sub.callback = callback;
+
+        // If we are connected, tell the service that we are watching.  If we aren't
+        // connected, the service will be told when we connect.
+        if (mState == CONNECT_STATE_CONNECTED && newSubscription) {
+            try {
+                mServiceBinder.addSubscription(parentUri, mServiceCallbacks);
+            } catch (RemoteException ex) {
+                // Process is crashing.  We will disconnect, and upon reconnect we will
+                // automatically reregister. So nothing to do here.
+                Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + parentUri);
+            }
+        }
+    }
+
+    /**
+     * Unsubscribes for changes to the children of the specified Uri.
+     * <p>
+     * The query callback will no longer be invoked for results associated with
+     * this Uri once this method returns.
+     * </p>
+     *
+     * @param parentUri The uri of the parent media item whose list of children
+     * will be unsubscribed.
+     */
+    public void unsubscribe(@NonNull Uri parentUri) {
+        // Check arguments.
+        if (parentUri == null) {
+            throw new IllegalArgumentException("parentUri is null");
+        }
+
+        // Remove from our list.
+        final Subscription sub = mSubscriptions.remove(parentUri);
+
+        // Tell the service if necessary.
+        if (mState == CONNECT_STATE_CONNECTED && sub != null) {
+            try {
+                mServiceBinder.removeSubscription(parentUri, mServiceCallbacks);
+            } catch (RemoteException ex) {
+                // Process is crashing.  We will disconnect, and upon reconnect we will
+                // automatically reregister. So nothing to do here.
+                Log.d(TAG, "removeSubscription failed with RemoteException parentUri=" + parentUri);
+            }
+        }
+    }
+
+    /**
+     * Loads the thumbnail of a media item.
+     *
+     * @param uri The uri of the media item.
+     * @param width The preferred width of the icon in dp.
+     * @param height The preferred width of the icon in dp.
+     * @param density The preferred density of the icon. Must be one of the android
+     *      density buckets.
+     * @param callback The callback to receive the thumbnail.
+     *
+     * @throws IllegalStateException if not connected. TODO: Is this restriction necessary?
+     */
+    public void loadThumbnail(@NonNull Uri uri, int width, int height, int density,
+            @NonNull ThumbnailCallback callback) {
+        throw new RuntimeException("implement me");
+    }
+
+    /**
+     * For debugging.
+     */
+    private static String getStateLabel(int state) {
+        switch (state) {
+            case CONNECT_STATE_DISCONNECTED:
+                return "CONNECT_STATE_DISCONNECTED";
+            case CONNECT_STATE_CONNECTING:
+                return "CONNECT_STATE_CONNECTING";
+            case CONNECT_STATE_CONNECTED:
+                return "CONNECT_STATE_CONNECTED";
+            case CONNECT_STATE_SUSPENDED:
+                return "CONNECT_STATE_SUSPENDED";
+            default:
+                return "UNKNOWN/" + state;
+        }
+    }
+
+    private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
+            final Uri root, final MediaSession.Token session) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // Check to make sure there hasn't been a disconnect or a different
+                // ServiceConnection.
+                if (!isCurrent(callback, "onConnect")) {
+                    return;
+                }
+                // Don't allow them to call us twice.
+                if (mState != CONNECT_STATE_CONNECTING) {
+                    Log.w(TAG, "onConnect from service while mState="
+                            + getStateLabel(mState) + "... ignoring");
+                    return;
+                }
+                mRootUri = root;
+                mMediaSessionToken = session;
+                mState = CONNECT_STATE_CONNECTED;
+
+                if (DBG) {
+                    Log.d(TAG, "ServiceCallbacks.onConnect...");
+                    dump();
+                }
+                mCallback.onConnected();
+
+                // we may receive some subscriptions before we are connected, so re-subscribe
+                // everything now
+                for (Uri uri : mSubscriptions.keySet()) {
+                    try {
+                        mServiceBinder.addSubscription(uri, mServiceCallbacks);
+                    } catch (RemoteException ex) {
+                        // Process is crashing.  We will disconnect, and upon reconnect we will
+                        // automatically reregister. So nothing to do here.
+                        Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + uri);
+                    }
+                }
+
+            }
+        });
+    }
+
+    private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Log.e(TAG, "onConnectFailed for " + mServiceComponent);
+
+                // Check to make sure there hasn't been a disconnect or a different
+                // ServiceConnection.
+                if (!isCurrent(callback, "onConnectFailed")) {
+                    return;
+                }
+                // Don't allow them to call us twice.
+                if (mState != CONNECT_STATE_CONNECTING) {
+                    Log.w(TAG, "onConnect from service while mState="
+                            + getStateLabel(mState) + "... ignoring");
+                    return;
+                }
+
+                // Clean up
+                forceCloseConnection();
+
+                // Tell the app.
+                mCallback.onConnectionFailed();
+            }
+        });
+    }
+
+    private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback, final Uri uri,
+            final ParceledListSlice list) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // Check that there hasn't been a disconnect or a different
+                // ServiceConnection.
+                if (!isCurrent(callback, "onLoadChildren")) {
+                    return;
+                }
+
+                List<MediaBrowserItem> data = list.getList();
+                if (DBG) {
+                    Log.d(TAG, "onLoadChildren for " + mServiceComponent + " uri=" + uri);
+                }
+                if (data == null) {
+                    data = Collections.emptyList();
+                }
+
+                // Check that the subscription is still subscribed.
+                final Subscription subscription = mSubscriptions.get(uri);
+                if (subscription == null) {
+                    if (DBG) {
+                        Log.d(TAG, "onLoadChildren for uri that isn't subscribed uri="
+                                + uri);
+                    }
+                    return;
+                }
+
+                // Tell the app.
+                subscription.callback.onChildrenLoaded(uri, data);
+            }
+        });
+    }
+
+
+    /**
+     * Return true if {@code callback} is the current ServiceCallbacks.  Also logs if it's not.
+     */
+    private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) {
+        if (mServiceCallbacks != callback) {
+            if (mState != CONNECT_STATE_DISCONNECTED) {
+                Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+                        + mServiceCallbacks + " this=" + this);
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private ServiceCallbacks getNewServiceCallbacks() {
+        return new ServiceCallbacks(this);
+    }
+
+    /**
+     * Log internal state.
+     * @hide
+     */
+    void dump() {
+        Log.d(TAG, "MediaBrowser...");
+        Log.d(TAG, "  mServiceComponent=" + mServiceComponent);
+        Log.d(TAG, "  mCallback=" + mCallback);
+        Log.d(TAG, "  mRootHints=" + mRootHints);
+        Log.d(TAG, "  mState=" + getStateLabel(mState));
+        Log.d(TAG, "  mServiceConnection=" + mServiceConnection);
+        Log.d(TAG, "  mServiceBinder=" + mServiceBinder);
+        Log.d(TAG, "  mServiceCallbacks=" + mServiceCallbacks);
+        Log.d(TAG, "  mRootUri=" + mRootUri);
+        Log.d(TAG, "  mMediaSessionToken=" + mMediaSessionToken);
+    }
+
+
+    /**
+     * Callbacks for connection related events.
+     */
+    public static class ConnectionCallback {
+        /**
+         * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed.
+         */
+        public void onConnected() {
+        }
+
+        /**
+         * Invoked when the client is disconnected from the media browser.
+         */
+        public void onConnectionSuspended() {
+        }
+
+        /**
+         * Invoked when the connection to the media browser failed.
+         */
+        public void onConnectionFailed() {
+        }
+    }
+
+    /**
+     * Callbacks for subscription related events.
+     */
+    public static abstract class SubscriptionCallback {
+        /**
+         * Called when the list of children is loaded or updated.
+         */
+        public void onChildrenLoaded(@NonNull Uri parentUri,
+                                     @NonNull List<MediaBrowserItem> children) {
+        }
+
+        /**
+         * Called when the Uri doesn't exist or other errors in subscribing.
+         * <p>
+         * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
+         * called, because some errors may heal themselves.
+         * </p>
+         */
+        public void onError(@NonNull Uri uri) {
+        }
+    }
+
+    /**
+     * Callbacks for thumbnail loading.
+     */
+    public static abstract class ThumbnailCallback {
+        /**
+         * Called when the thumbnail is loaded.
+         */
+        public void onThumbnailLoaded(@NonNull Uri uri, @NonNull Bitmap bitmap) {
+        }
+
+        /**
+         * Called when the Uri doesn’t exist or the bitmap cannot be loaded.
+         */
+        public void onError(@NonNull Uri uri) {
+        }
+    }
+
+    /**
+     * ServiceConnection to the other app.
+     */
+    private class MediaServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder binder) {
+            if (DBG) {
+                Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
+                        + " binder=" + binder);
+                dump();
+            }
+
+            // Make sure we are still the current connection, and that they haven't called
+            // disconnect().
+            if (!isCurrent("onServiceConnected")) {
+                return;
+            }
+
+            // Save their binder
+            mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
+
+            // We make a new mServiceCallbacks each time we connect so that we can drop
+            // responses from previous connections.
+            mServiceCallbacks = getNewServiceCallbacks();
+
+            // Call connect, which is async. When we get a response from that we will
+            // say that we're connected.
+            try {
+                if (DBG) {
+                    Log.d(TAG, "ServiceCallbacks.onConnect...");
+                    dump();
+                }
+                mServiceBinder.connect(mContext.getPackageName(), mRootHints, mServiceCallbacks);
+            } catch (RemoteException ex) {
+                // Connect failed, which isn't good. But the auto-reconnect on the service
+                // will take over and we will come back.  We will also get the
+                // onServiceDisconnected, which has all the cleanup code.  So let that do it.
+                Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+                if (DBG) {
+                    Log.d(TAG, "ServiceCallbacks.onConnect...");
+                    dump();
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DBG) {
+                Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
+                        + " this=" + this + " mServiceConnection=" + mServiceConnection);
+                dump();
+            }
+
+            // Make sure we are still the current connection, and that they haven't called
+            // disconnect().
+            if (!isCurrent("onServiceDisconnected")) {
+                return;
+            }
+
+            // Clear out what we set in onServiceConnected
+            mServiceBinder = null;
+            mServiceCallbacks = null;
+
+            // And tell the app that it's suspended.
+            mState = CONNECT_STATE_SUSPENDED;
+            mCallback.onConnectionSuspended();
+        }
+
+        /**
+         * Return true if this is the current ServiceConnection.  Also logs if it's not.
+         */
+        private boolean isCurrent(String funcName) {
+            if (mServiceConnection != this) {
+                if (mState != CONNECT_STATE_DISCONNECTED) {
+                    // Check mState, because otherwise this log is noisy.
+                    Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+                            + mServiceConnection + " this=" + this);
+                }
+                return false;
+            }
+            return true;
+        }
+    };
+
+    /**
+     * Callbacks from the service.
+     */
+    private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub {
+        private WeakReference<MediaBrowser> mMediaBrowser;
+
+        public ServiceCallbacks(MediaBrowser mediaBrowser) {
+            mMediaBrowser = new WeakReference<MediaBrowser>(mediaBrowser);
+        }
+
+        /**
+         * The other side has acknowledged our connection.  The parameters to this function
+         * are the initial data as requested.
+         */
+        @Override
+        public void onConnect(final Uri root, final MediaSession.Token session) {
+            MediaBrowser mediaBrowser = mMediaBrowser.get();
+            if (mediaBrowser != null) {
+                mediaBrowser.onServiceConnected(this, root, session);
+            }
+        }
+
+        /**
+         * The other side does not like us.  Tell the app via onConnectionFailed.
+         */
+        @Override
+        public void onConnectFailed() {
+            MediaBrowser mediaBrowser = mMediaBrowser.get();
+            if (mediaBrowser != null) {
+                mediaBrowser.onConnectionFailed(this);
+            }
+        }
+
+        @Override
+        public void onLoadChildren(final Uri uri, final ParceledListSlice list) {
+            MediaBrowser mediaBrowser = mMediaBrowser.get();
+            if (mediaBrowser != null) {
+                mediaBrowser.onLoadChildren(this, uri, list);
+            }
+        }
+    }
+
+    private static class Subscription {
+        final Uri uri;
+        SubscriptionCallback callback;
+
+        Subscription(Uri u) {
+            this.uri = u;
+        }
+    }
+}
diff --git a/media/java/android/media/browse/MediaBrowserItem.java b/media/java/android/media/browse/MediaBrowserItem.java
new file mode 100644
index 0000000..119f687
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowserItem.java
@@ -0,0 +1,241 @@
+/*
+ * 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.browse;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes a media item in the list of items offered by a {@link MediaBrowserService}.
+ */
+public final class MediaBrowserItem implements Parcelable {
+    private final Uri mUri;
+    private final int mFlags;
+    private final CharSequence mTitle;
+    private final CharSequence mSummary;
+    private final Bundle mExtras;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
+    public @interface Flags { }
+
+    /**
+     * Flag: Indicates that the item has children of its own.
+     */
+    public static final int FLAG_BROWSABLE = 1 << 0;
+
+    /**
+     * Flag: Indicates that the item is playable.
+     * <p>
+     * The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
+     * to start playing it.
+     * </p>
+     */
+    public static final int FLAG_PLAYABLE = 1 << 1;
+
+    /**
+     * Initialize a MediaBrowserItem object.
+     */
+    private MediaBrowserItem(@NonNull Uri uri, int flags, @NonNull CharSequence title,
+            CharSequence summary, Bundle extras) {
+        if (uri == null) {
+            throw new IllegalArgumentException("uri can not be null");
+        }
+        if (title == null) {
+            throw new IllegalArgumentException("title can not be null");
+        }
+        mUri = uri;
+        mFlags = flags;
+        mTitle = title;
+        mSummary = summary;
+        mExtras = extras;
+    }
+
+    /**
+     * Private constructor.
+     */
+    private MediaBrowserItem(Parcel in) {
+        mUri = Uri.CREATOR.createFromParcel(in);
+        mFlags = in.readInt();
+        mTitle = in.readCharSequence();
+        if (in.readInt() != 0) {
+            mSummary = in.readCharSequence();
+        } else {
+            mSummary = null;
+        }
+        if (in.readInt() != 0) {
+            mExtras = Bundle.CREATOR.createFromParcel(in);
+        } else {
+            mExtras = null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        mUri.writeToParcel(out, flags);
+        out.writeInt(mFlags);
+        out.writeCharSequence(mTitle);
+        if (mSummary != null) {
+            out.writeInt(1);
+            out.writeCharSequence(mSummary);
+        } else {
+            out.writeInt(0);
+        }
+        if (mExtras != null) {
+            out.writeInt(1);
+            mExtras.writeToParcel(out, flags);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    public static final Parcelable.Creator<MediaBrowserItem> CREATOR =
+            new Parcelable.Creator<MediaBrowserItem>() {
+        @Override
+        public MediaBrowserItem createFromParcel(Parcel in) {
+            return new MediaBrowserItem(in);
+        }
+
+        @Override
+        public MediaBrowserItem[] newArray(int size) {
+            return new MediaBrowserItem[size];
+        }
+    };
+
+    /**
+     * Gets the Uri of the item.
+     */
+    public @NonNull Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Gets the flags of the item.
+     */
+    public @Flags int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Returns whether this item is browsable.
+     * @see #FLAG_BROWSABLE
+     */
+    public boolean isBrowsable() {
+        return (mFlags & FLAG_BROWSABLE) != 0;
+    }
+
+    /**
+     * Returns whether this item is playable.
+     * @see #FLAG_PLAYABLE
+     */
+    public boolean isPlayable() {
+        return (mFlags & FLAG_PLAYABLE) != 0;
+    }
+
+    /**
+     * Gets the title of the item.
+     * @more
+     * The title will be shown as the first line of text when
+     * describing each item to the user.
+     */
+    public @NonNull CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Gets summary of the item, or null if none.
+     * @more
+     * The summary will be shown as the second line of text when
+     * describing each item to the user.
+     */
+    public @Nullable CharSequence getSummary() {
+        return mSummary;
+    }
+
+    /**
+     * Gets additional service-specified extras about the
+     * item or its content, or null if none.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Builder for {@link MediaBrowserItem} objects.
+     */
+    public static final class Builder {
+        private final Uri mUri;
+        private final int mFlags;
+        private final CharSequence mTitle;
+        private CharSequence mSummary;
+        private Bundle mExtras;
+
+        /**
+         * Creates an item builder.
+         */
+        public Builder(@NonNull Uri uri, @Flags int flags, @NonNull CharSequence title) {
+            if (uri == null) {
+                throw new IllegalArgumentException("uri can not be null");
+            }
+            if (title == null) {
+                throw new IllegalArgumentException("title can not be null");
+            }
+            mUri = uri;
+            mFlags = flags;
+            mTitle = title;
+        }
+
+        /**
+         * Sets summary of the item, or null if none.
+         */
+        public @NonNull Builder setSummary(@Nullable CharSequence summary) {
+            mSummary = summary;
+            return this;
+        }
+
+        /**
+        * Sets additional service-specified extras about the
+        * item or its content, or null if none.
+        */
+        public @NonNull Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+        * Builds the item.
+        */
+        public @NonNull MediaBrowserItem build() {
+            return new MediaBrowserItem(mUri, mFlags, mTitle, mSummary, mExtras);
+        }
+    }
+}
+
diff --git a/media/java/android/media/browse/MediaBrowserService.java b/media/java/android/media/browse/MediaBrowserService.java
new file mode 100644
index 0000000..ceb4b03
--- /dev/null
+++ b/media/java/android/media/browse/MediaBrowserService.java
@@ -0,0 +1,363 @@
+/*
+ * 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.browse;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Base class for media browse services.
+ * <p>
+ * Media browse services enable applications to browse media content provided by an application
+ * and ask the application to start playing it.  They may also be used to control content that
+ * is already playing by way of a {@link MediaSession}.
+ * </p>
+ *
+ * To extend this class, you must declare the service in your manifest file with
+ * an intent filter with the {@link #SERVICE_ACTION} action.
+ *
+ * For example:
+ * </p><pre>
+ * &lt;service android:name=".MyMediaBrowserService"
+ *          android:label="&#64;string/service_name" >
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.media.browse.MediaBrowserService" />
+ *     &lt;/intent-filter>
+ * &lt;/service>
+ * </pre>
+ *
+ */
+public abstract class MediaBrowserService extends Service {
+    private static final String TAG = "MediaBrowserService";
+
+    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
+    private final Handler mHandler = new Handler();
+    private ServiceBinder mBinder;
+    MediaSession.Token mSession;
+
+    /**
+     * All the info about a connection.
+     */
+    private class ConnectionRecord {
+        String pkg;
+        Bundle rootHints;
+        IMediaBrowserServiceCallbacks callbacks;
+        Uri root;
+        HashSet<Uri> subscriptions = new HashSet();
+    }
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
+
+    private class ServiceBinder extends IMediaBrowserService.Stub {
+        @Override
+        public void connect(final String pkg, final Bundle rootHints,
+                final IMediaBrowserServiceCallbacks callbacks) {
+
+            final int uid = Binder.getCallingUid();
+            if (!isValidPackage(pkg, uid)) {
+                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+                        + " package=" + pkg);
+            }
+
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final IBinder b = callbacks.asBinder();
+
+                        // Clear out the old subscriptions.  We are getting new ones.
+                        mConnections.remove(b);
+
+                        final ConnectionRecord connection = new ConnectionRecord();
+                        connection.pkg = pkg;
+                        connection.rootHints = rootHints;
+                        connection.callbacks = callbacks;
+
+                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
+
+                        // If they didn't return something, don't allow this client.
+                        if (connection.root == null) {
+                            Log.i(TAG, "No root for client " + pkg + " from service "
+                                    + getClass().getName());
+                            try {
+                                callbacks.onConnectFailed();
+                            } catch (RemoteException ex) {
+                                Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
+                                        + "pkg=" + pkg);
+                            }
+                        } else {
+                            try {
+                                mConnections.put(b, connection);
+                                callbacks.onConnect(connection.root, mSession);
+                            } catch (RemoteException ex) {
+                                Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+                                        + "pkg=" + pkg);
+                                mConnections.remove(b);
+                            }
+                        }
+                    }
+                });
+        }
+
+        @Override
+        public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final IBinder b = callbacks.asBinder();
+
+                        // Clear out the old subscriptions.  We are getting new ones.
+                        final ConnectionRecord old = mConnections.remove(b);
+                        if (old != null) {
+                            // TODO
+                        }
+                    }
+                });
+        }
+
+
+        @Override
+        public void addSubscription(final Uri uri, final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final IBinder b = callbacks.asBinder();
+
+                        // Get the record for the connection
+                        final ConnectionRecord connection = mConnections.get(b);
+                        if (connection == null) {
+                            Log.w(TAG, "addSubscription for callback that isn't registered uri="
+                                + uri);
+                            return;
+                        }
+
+                        MediaBrowserService.this.addSubscription(uri, connection);
+                    }
+                });
+        }
+
+        @Override
+        public void removeSubscription(final Uri uri,
+                final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final IBinder b = callbacks.asBinder();
+
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "removeSubscription for callback that isn't registered uri="
+                                + uri);
+                        return;
+                    }
+                    if (!connection.subscriptions.remove(uri)) {
+                        Log.w(TAG, "removeSubscription called for " + uri
+                                + " which is not subscribed");
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mBinder = new ServiceBinder();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SERVICE_ACTION.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+    }
+
+    /**
+     * Called to get the root uri for browsing by a particular client.
+     * <p>
+     * The implementation should verify that the client package has
+     * permission to access browse media information before returning
+     * the root uri; it should return null if the client is not
+     * allowed to access this information.
+     * </p>
+     *
+     * @param clientPackageName The package name of the application
+     * which is requesting access to browse media.
+     * @param clientUid The uid of the application which is requesting
+     * access to browse media.
+     * @param rootHints An optional bundle of service-specific arguments to send
+     * to the media browse service when connecting and retrieving the root uri
+     * for browsing, or null if none.  The contents of this bundle may affect
+     * the information returned when browsing.
+     */
+    public abstract @Nullable Uri onGetRoot(@NonNull String clientPackageName, int clientUid,
+            @Nullable Bundle rootHints);
+
+    /**
+     * Called to get information about the children of a media item.
+     *
+     * @param parentUri The uri of the parent media item whose
+     * children are to be queried.
+     * @return The list of children, or null if the uri is invalid.
+     */
+    public abstract @Nullable List<MediaBrowserItem> onLoadChildren(@NonNull Uri parentUri);
+
+    /**
+     * Called to get the thumbnail of a particular media item.
+     *
+     * @param uri The uri of the media item.
+     * @param width The requested width of the icon in dp.
+     * @param height The requested height of the icon in dp.
+     * @param density The requested density of the icon. This is the approximate density of the
+     *              screen on which the icon will be displayed.  This density will be one of
+     *              the android density buckets.
+     * @return The file descriptor of the thumbnail, which may then be loaded
+     *          using a bitmap factory, or null if the item does not have a thumbnail.
+     */
+    public abstract @Nullable Bitmap onGetThumbnail(@NonNull Uri uri,
+            int width, int height, int density);
+
+    /**
+     * Call to set the media session.
+     * <p>
+     * This must be called before onCreate returns.
+     *
+     * @return The media session token, must not be null.
+     */
+    public void setSessionToken(MediaSession.Token token) {
+        if (token == null) {
+            throw new IllegalStateException(this.getClass().getName()
+                    + ".onCreateSession() set invalid MediaSession.Token");
+        }
+        mSession = token;
+    }
+
+    /**
+     * Gets the session token, or null if it has not yet been created
+     * or if it has been destroyed.
+     */
+    public @Nullable MediaSession.Token getSessionToken() {
+        return mSession;
+    }
+
+    /**
+     * Notifies all connected media browsers that the content of
+     * the browse service has changed in some way.
+     * This will cause browsers to fetch subscribed content again.
+     */
+    public void notifyChange() {
+        throw new RuntimeException("implement me");
+    }
+
+    /**
+     * Notifies all connected media browsers that the children of
+     * the specified Uri have changed in some way.
+     * This will cause browsers to fetch subscribed content again.
+     *
+     * @param parentUri The uri of the parent media item whose
+     * children changed.
+     */
+    public void notifyChildrenChanged(@NonNull Uri parentUri) {
+        throw new RuntimeException("implement me");
+    }
+
+    /**
+     * Return whether the given package is one of the ones that is owned by the uid.
+     */
+    private boolean isValidPackage(String pkg, int uid) {
+        if (pkg == null) {
+            return false;
+        }
+        final PackageManager pm = getPackageManager();
+        final String[] packages = pm.getPackagesForUid(uid);
+        final int N = packages.length;
+        for (int i=0; i<N; i++) {
+            if (packages[i].equals(pkg)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Save the subscription and if it is a new subscription send the results.
+     */
+    private void addSubscription(Uri uri, ConnectionRecord connection) {
+        // Save the subscription
+        final boolean added = connection.subscriptions.add(uri);
+
+        // If this is a new subscription, send the results
+        if (added) {
+            performLoadChildren(uri, connection);
+        }
+    }
+
+    /**
+     * Call onLoadChildren and then send the results back to the connection.
+     * <p>
+     * Callers must make sure that this connection is still connected.
+     * <p>
+     * TODO: Think about caching and combining these calls.
+     */
+    private void performLoadChildren(Uri uri, ConnectionRecord connection) {
+        final List<MediaBrowserItem> list = onLoadChildren(uri);
+        if (list == null) {
+            throw new IllegalStateException("onLoadChildren returned null for uri " + uri);
+        }
+        final ParceledListSlice<MediaBrowserItem> pls = new ParceledListSlice(list);
+        try {
+            connection.callbacks.onLoadChildren(uri, pls);
+        } catch (RemoteException ex) {
+            // The other side is in the process of crashing.
+            Log.w(TAG, "Calling onLoadChildren() failed for uri=" + uri
+                    + " package=" + connection.pkg);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 78e0404..acdfbe19 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -340,85 +340,85 @@
         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
 
         /** A generic channel type. */
-        public static final int TYPE_OTHER = 0x0;
+        public static final String TYPE_OTHER = "TYPE_OTHER";
 
         /** The channel type for NTSC. */
-        public static final int TYPE_NTSC = 0x1;
+        public static final String TYPE_NTSC = "TYPE_NTSC";
 
         /** The channel type for PAL. */
-        public static final int TYPE_PAL = 0x2;
+        public static final String TYPE_PAL = "TYPE_PAL";
 
         /** The channel type for SECAM. */
-        public static final int TYPE_SECAM = 0x3;
+        public static final String TYPE_SECAM = "TYPE_SECAM";
 
         /** The channel type for DVB-T (terrestrial). */
-        public static final int TYPE_DVB_T = 0x00010000;
+        public static final String TYPE_DVB_T = "TYPE_DVB_T";
 
         /** The channel type for DVB-T2 (terrestrial). */
-        public static final int TYPE_DVB_T2 = 0x00010001;
+        public static final String TYPE_DVB_T2 = "TYPE_DVB_T2";
 
         /** The channel type for DVB-S (satellite). */
-        public static final int TYPE_DVB_S = 0x00010100;
+        public static final String TYPE_DVB_S = "TYPE_DVB_S";
 
         /** The channel type for DVB-S2 (satellite). */
-        public static final int TYPE_DVB_S2 = 0x00010101;
+        public static final String TYPE_DVB_S2 = "TYPE_DVB_S2";
 
         /** The channel type for DVB-C (cable). */
-        public static final int TYPE_DVB_C = 0x00010200;
+        public static final String TYPE_DVB_C = "TYPE_DVB_C";
 
         /** The channel type for DVB-C2 (cable). */
-        public static final int TYPE_DVB_C2 = 0x00010201;
+        public static final String TYPE_DVB_C2 = "TYPE_DVB_C2";
 
         /** The channel type for DVB-H (handheld). */
-        public static final int TYPE_DVB_H = 0x00010300;
+        public static final String TYPE_DVB_H = "TYPE_DVB_H";
 
         /** The channel type for DVB-SH (satellite). */
-        public static final int TYPE_DVB_SH = 0x00010400;
+        public static final String TYPE_DVB_SH = "TYPE_DVB_SH";
 
         /** The channel type for ATSC (terrestrial). */
-        public static final int TYPE_ATSC_T = 0x00020000;
+        public static final String TYPE_ATSC_T = "TYPE_ATSC_T";
 
         /** The channel type for ATSC (cable). */
-        public static final int TYPE_ATSC_C = 0x00020200;
+        public static final String TYPE_ATSC_C = "TYPE_ATSC_C";
 
         /** The channel type for ATSC-M/H (mobile/handheld). */
-        public static final int TYPE_ATSC_M_H = 0x00020300;
+        public static final String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
 
         /** The channel type for ISDB-T (terrestrial). */
-        public static final int TYPE_ISDB_T = 0x00030000;
+        public static final String TYPE_ISDB_T = "TYPE_ISDB_T";
 
         /** The channel type for ISDB-Tb (Brazil). */
-        public static final int TYPE_ISDB_TB = 0x00030100;
+        public static final String TYPE_ISDB_TB = "TYPE_ISDB_TB";
 
         /** The channel type for ISDB-S (satellite). */
-        public static final int TYPE_ISDB_S = 0x00030200;
+        public static final String TYPE_ISDB_S = "TYPE_ISDB_S";
 
         /** The channel type for ISDB-C (cable). */
-        public static final int TYPE_ISDB_C = 0x00030300;
+        public static final String TYPE_ISDB_C = "TYPE_ISDB_C";
 
         /** The channel type for 1seg (handheld). */
-        public static final int TYPE_1SEG = 0x00030400;
+        public static final String TYPE_1SEG = "TYPE_1SEG";
 
         /** The channel type for DTMB (terrestrial). */
-        public static final int TYPE_DTMB = 0x00040000;
+        public static final String TYPE_DTMB = "TYPE_DTMB";
 
         /** The channel type for CMMB (handheld). */
-        public static final int TYPE_CMMB = 0x00040100;
+        public static final String TYPE_CMMB = "TYPE_CMMB";
 
         /** The channel type for T-DMB (terrestrial). */
-        public static final int TYPE_T_DMB = 0x00050000;
+        public static final String TYPE_T_DMB = "TYPE_T_DMB";
 
         /** The channel type for S-DMB (satellite). */
-        public static final int TYPE_S_DMB = 0x00050100;
+        public static final String TYPE_S_DMB = "TYPE_S_DMB";
 
         /** A generic service type. */
-        public static final int SERVICE_TYPE_OTHER = 0x0;
+        public static final String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
 
         /** The service type for regular TV channels that have both audio and video. */
-        public static final int SERVICE_TYPE_AUDIO_VIDEO = 0x1;
+        public static final String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
 
         /** The service type for radio channels that have audio only. */
-        public static final int SERVICE_TYPE_AUDIO = 0x2;
+        public static final String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
 
         /** The video format for 240p. */
         public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
@@ -521,7 +521,7 @@
          * </p><p>
          * This is a required field.
          * </p><p>
-         * Type: INTEGER
+         * Type: TEXT
          * </p>
          */
         public static final String COLUMN_TYPE = "type";
@@ -537,7 +537,7 @@
          * </p><p>
          * This is a required field.
          * </p><p>
-         * Type: INTEGER
+         * Type: TEXT
          * </p>
          */
         public static final String COLUMN_SERVICE_TYPE = "service_type";
@@ -815,6 +815,36 @@
         public static final String COLUMN_TITLE = "title";
 
         /**
+         * The season number of this TV program for episodic TV shows.
+         * <p>
+         * Can be empty.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         **/
+        public static final String COLUMN_SEASON_NUMBER = "season_number";
+
+        /**
+         * The episode number of this TV program for episodic TV shows.
+         * <p>
+         * Can be empty.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         **/
+        public static final String COLUMN_EPISODE_NUMBER = "episode_number";
+
+        /**
+         * The episode title of this TV program for episodic TV shows.
+         * <p>
+         * Can be empty.
+         * </p><p>
+         * Type: TEXT
+         * </p>
+         **/
+        public static final String COLUMN_EPISODE_TITLE = "episode_title";
+
+        /**
          * The start time of this TV program, in milliseconds since the epoch.
          * <p>
          * Type: INTEGER (long)
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 86fd4ff..331283e 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -33,6 +33,7 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.SparseIntArray;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -47,60 +48,55 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "TvInputInfo";
 
+    // Should be in sync with frameworks/base/core/res/res/values/attrs.xml
     /**
-     * TV input type: the TV input service is not handling input from hardware. For example,
-     * services showing streaming from the internet falls into this type.
+     * TV input type: the TV input service is a tuner which provides channels.
      */
-    public static final int TYPE_VIRTUAL = 0;
-
-    // Should be in sync with TvInputHardwareInfo.
-
+    public static final int TYPE_TUNER = 0;
     /**
      * TV input type: a generic hardware TV input type.
      */
-    public static final int TYPE_OTHER_HARDWARE = TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE;
-    /**
-     * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner)
-     */
-    public static final int TYPE_TUNER = TvInputHardwareInfo.TV_INPUT_TYPE_TUNER;
+    public static final int TYPE_OTHER = 1000;
     /**
      * TV input type: the TV input service represents a composite port.
      */
-    public static final int TYPE_COMPOSITE = TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE;
+    public static final int TYPE_COMPOSITE = 1001;
     /**
      * TV input type: the TV input service represents a SVIDEO port.
      */
-    public static final int TYPE_SVIDEO = TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO;
+    public static final int TYPE_SVIDEO = 1002;
     /**
      * TV input type: the TV input service represents a SCART port.
      */
-    public static final int TYPE_SCART = TvInputHardwareInfo.TV_INPUT_TYPE_SCART;
+    public static final int TYPE_SCART = 1003;
     /**
      * TV input type: the TV input service represents a component port.
      */
-    public static final int TYPE_COMPONENT = TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT;
+    public static final int TYPE_COMPONENT = 1004;
     /**
      * TV input type: the TV input service represents a VGA port.
      */
-    public static final int TYPE_VGA = TvInputHardwareInfo.TV_INPUT_TYPE_VGA;
+    public static final int TYPE_VGA = 1005;
     /**
      * TV input type: the TV input service represents a DVI port.
      */
-    public static final int TYPE_DVI = TvInputHardwareInfo.TV_INPUT_TYPE_DVI;
+    public static final int TYPE_DVI = 1006;
     /**
      * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
      */
-    public static final int TYPE_HDMI = TvInputHardwareInfo.TV_INPUT_TYPE_HDMI;
+    public static final int TYPE_HDMI = 1007;
     /**
      * TV input type: the TV input service represents a display port.
      */
-    public static final int TYPE_DISPLAY_PORT = TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT;
+    public static final int TYPE_DISPLAY_PORT = 1008;
 
     /**
      * The ID of the TV input to provide to the setup activity and settings activity.
      */
     public static final String EXTRA_INPUT_ID = "inputId";
 
+    private static SparseIntArray sHardwareTypeToTvInputType = new SparseIntArray();
+
     private static final String XML_START_TAG_NAME = "tv-input";
 
     private final ResolveInfo mService;
@@ -110,7 +106,22 @@
     // Attributes from XML meta data.
     private String mSetupActivity;
     private String mSettingsActivity;
-    private int mType = TYPE_VIRTUAL;
+    private int mType = TYPE_TUNER;
+
+    static {
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
+                TYPE_OTHER);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_TUNER, TYPE_TUNER);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE, TYPE_COMPOSITE);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO, TYPE_SVIDEO);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SCART, TYPE_SCART);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT, TYPE_COMPONENT);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_VGA, TYPE_VGA);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DVI, TYPE_DVI);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI, TYPE_HDMI);
+        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT,
+                TYPE_DISPLAY_PORT);
+    }
 
     /**
      * Create a new instance of the TvInputInfo class,
@@ -123,7 +134,7 @@
             throws XmlPullParserException, IOException {
         return createTvInputInfo(context, service, generateInputIdForComponentName(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)),
-                null);
+                null, TYPE_TUNER);
     }
 
     /**
@@ -138,7 +149,7 @@
             HdmiCecDeviceInfo cecInfo, String parentId) throws XmlPullParserException, IOException {
         return createTvInputInfo(context, service, generateInputIdForHdmiCec(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
-                cecInfo), parentId);
+                cecInfo), parentId, TYPE_HDMI);
     }
 
     /**
@@ -151,13 +162,14 @@
      */
     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
             TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException {
+        int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER);
         return createTvInputInfo(context, service, generateInputIdForHardware(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
-                hardwareInfo), null);
+                hardwareInfo), null, inputType);
     }
 
     private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
-            String id, String parentId) throws XmlPullParserException, IOException {
+            String id, String parentId, int inputType) throws XmlPullParserException, IOException {
         ServiceInfo si = service.serviceInfo;
         PackageManager pm = context.getPackageManager();
         XmlResourceParser parser = null;
@@ -182,7 +194,7 @@
                         "Meta-data does not start with tv-input-service tag in " + si.name);
             }
 
-            TvInputInfo input = new TvInputInfo(service, id, parentId);
+            TvInputInfo input = new TvInputInfo(service, id, parentId, inputType);
             TypedArray sa = res.obtainAttributes(attrs,
                     com.android.internal.R.styleable.TvInputService);
             input.mSetupActivity = sa.getString(
@@ -196,14 +208,6 @@
                 Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
                         + si.name);
             }
-            if (pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, si.packageName)
-                    == PackageManager.PERMISSION_GRANTED) {
-                input.mType = sa.getInt(
-                        com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
-                if (DEBUG) {
-                    Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
-                }
-            }
             sa.recycle();
 
             return input;
@@ -222,11 +226,13 @@
      * @param service The ResolveInfo returned from the package manager about this TV input service.
      * @param id ID of this TV input. Should be generated via generateInputId*().
      * @param parentId ID of this TV input's parent input. {@code null} if none exists.
+     * @param type The type of this TV input service.
      */
-    private TvInputInfo(ResolveInfo service, String id, String parentId) {
+    private TvInputInfo(ResolveInfo service, String id, String parentId, int type) {
         mService = service;
         mId = id;
         mParentId = parentId;
+        mType = type;
     }
 
     /**
@@ -314,12 +320,7 @@
      * @see TvContract#buildChannelUriForPassthroughTvInput(String)
      */
     public boolean isPassthroughInputType() {
-        if (mType == TYPE_HDMI || mType == TYPE_DISPLAY_PORT || mType == TYPE_SCART
-                || mType == TYPE_DVI || mType == TYPE_VGA || mType == TYPE_COMPONENT
-                || mType == TYPE_COMPOSITE || mType == TYPE_SVIDEO) {
-            return true;
-        }
-        return false;
+        return mType != TYPE_TUNER;
     }
 
     /**
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index bda3b6b..89b2893 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -32,10 +32,17 @@
     jmethodID   mPostEvent;
     jclass      mSoundPoolClass;
 } fields;
-
 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
     return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
 }
+static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
+struct audio_attributes_fields_t {
+    jfieldID  fieldUsage;        // AudioAttributes.mUsage
+    jfieldID  fieldContentType;  // AudioAttributes.mContentType
+    jfieldID  fieldFlags;        // AudioAttributes.mFlags
+    jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
+};
+static audio_attributes_fields_t javaAudioAttrFields;
 
 // ----------------------------------------------------------------------------
 static jint
@@ -176,10 +183,30 @@
 }
 
 static jint
-android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
+android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
+        jint maxChannels, jobject jaa)
 {
+    if (jaa == 0) {
+        ALOGE("Error creating SoundPool: invalid audio attributes");
+        return -1;
+    }
+
+    audio_attributes_t *paa = NULL;
+    // read the AudioAttributes values
+    paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+    const jstring jtags =
+            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
+    const char* tags = env->GetStringUTFChars(jtags, NULL);
+    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+    strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+    env->ReleaseStringUTFChars(jtags, tags);
+    paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
+    paa->content_type =
+            (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
+    paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+
     ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
-    SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
+    SoundPool *ap = new SoundPool(maxChannels, paa);
     if (ap == NULL) {
         return -1;
     }
@@ -190,6 +217,10 @@
     // set callback with weak reference
     jobject globalWeakRef = env->NewGlobalRef(weakRef);
     ap->setCallback(android_media_callback, globalWeakRef);
+
+    // audio attributes were copied in SoundPool creation
+    free(paa);
+
     return 0;
 }
 
@@ -270,7 +301,7 @@
         (void *)android_media_SoundPool_SoundPoolImpl_setRate
     },
     {   "native_setup",
-        "(Ljava/lang/Object;III)I",
+        "(Ljava/lang/Object;ILjava/lang/Object;)I",
         (void*)android_media_SoundPool_SoundPoolImpl_native_setup
     },
     {   "release",
@@ -289,27 +320,27 @@
 
     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
         ALOGE("ERROR: GetEnv failed\n");
-        goto bail;
+        return result;
     }
     assert(env != NULL);
 
     clazz = env->FindClass(kClassPathName);
     if (clazz == NULL) {
         ALOGE("Can't find %s", kClassPathName);
-        goto bail;
+        return result;
     }
 
     fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
     if (fields.mNativeContext == NULL) {
         ALOGE("Can't find SoundPoolImpl.mNativeContext");
-        goto bail;
+        return result;
     }
 
     fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.mPostEvent == NULL) {
         ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
-        goto bail;
+        return result;
     }
 
     // create a reference to class. Technically, we're leaking this reference
@@ -317,11 +348,29 @@
     fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
 
     if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
-        goto bail;
+        return result;
+
+    // Get the AudioAttributes class and fields
+    jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
+    if (audioAttrClass == NULL) {
+        ALOGE("Can't find %s", kAudioAttributesClassPathName);
+        return result;
+    }
+    jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
+    javaAudioAttrFields.fieldUsage = env->GetFieldID(audioAttributesClassRef, "mUsage", "I");
+    javaAudioAttrFields.fieldContentType
+                                   = env->GetFieldID(audioAttributesClassRef, "mContentType", "I");
+    javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
+    javaAudioAttrFields.fieldFormattedTags =
+            env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
+    env->DeleteGlobalRef(audioAttributesClassRef);
+    if (javaAudioAttrFields.fieldUsage == NULL || javaAudioAttrFields.fieldContentType == NULL
+            || javaAudioAttrFields.fieldFlags == NULL
+            || javaAudioAttrFields.fieldFormattedTags == NULL) {
+        ALOGE("Can't initialize AudioAttributes fields");
+        return result;
+    }
 
     /* success -- return valid version number */
-    result = JNI_VERSION_1_4;
-
-bail:
-    return result;
+    return JNI_VERSION_1_4;
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index 150ab74..02a441b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,9 +16,11 @@
 
 package com.android.keyguard;
 
+import android.app.AlarmClockInfo;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
+import android.provider.AlarmClock;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.util.AttributeSet;
@@ -121,15 +123,24 @@
 
     void refreshAlarmStatus() {
         // Update Alarm status
-        String nextAlarm = mLockPatternUtils.getNextAlarm();
-        if (!TextUtils.isEmpty(nextAlarm)) {
-            mAlarmStatusView.setText(nextAlarm);
+        AlarmClockInfo nextAlarm = mLockPatternUtils.getNextAlarm();
+        if (nextAlarm != null) {
+            mAlarmStatusView.setText(formatNextAlarm(nextAlarm));
             mAlarmStatusView.setVisibility(View.VISIBLE);
         } else {
             mAlarmStatusView.setVisibility(View.GONE);
         }
     }
 
+    String formatNextAlarm(AlarmClockInfo info) {
+        if (info == null) {
+            return "";
+        }
+        String skeleton = DateFormat.is24HourFormat(mContext) ? "EHm" : "Ehma";
+        String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+        return DateFormat.format(pattern, info.getTriggerTime()).toString();
+    }
+
     private void updateOwnerInfo() {
         if (mOwnerInfo == null) return;
         String ownerInfo = getOwnerInfo();
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index a3a1f8c..3fbd4d8 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -22,8 +22,6 @@
 
 LOCAL_PACKAGE_NAME := PrintSpooler
 
-LOCAL_JAVA_LIBRARIES := framework-base
-
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
 
 include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 526aa5e..403860c 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -40,6 +40,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textSize="14sp"
-            android:textColor="@color/qs_user_detail_name" />
+            android:textColor="@color/qs_user_detail_name"
+            android:gravity="center_horizontal" />
 
 </com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 46057af..cf2f5d3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -671,12 +671,34 @@
     <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
 
     <!-- Related to user switcher --><skip/>
-    <!-- Name for the guest user -->
+    <!-- Name for the guest user [CHAR LIMIT=35] -->
     <string name="guest_nickname">Guest</string>
 
-    <!-- Label for adding a new guest -->
+    <!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
     <string name="guest_new_guest">+ Guest</string>
 
+    <!-- Label for exiting guest session in the user switcher [CHAR LIMIT=35] -->
+    <string name="guest_exit_guest">Exit guest</string>
+
+    <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
+    <string name="guest_exit_guest_dialog_title">Exiting guest session?</string>
+
+    <!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
+    <string name="guest_exit_guest_dialog_message">Exiting the guest session will remove local data.</string>
+
+    <!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
+    <string name="guest_wipe_session_title">Welcome back, guest!</string>
+
+    <!-- Message of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
+    <string name="guest_wipe_session_message">Do you want to start a new session?</string>
+
+    <!-- Notification when resuming an existing guest session: Action that starts a new session [CHAR LIMIT=35] -->
+    <string name="guest_wipe_session_wipe">Yes</string>
+
+    <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
+    <string name="guest_wipe_session_dontwipe">No, thanks</string>
+
+
     <!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
     <plurals name="zen_mode_duration_minutes">
         <item quantity="one">For one minute</item>
@@ -704,8 +726,6 @@
     <!-- 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>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index abf9337..0b8f876 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -275,4 +275,7 @@
         <item name="android:textStyle">italic</item>
         <item name="android:textColor">#60000000</item>
     </style>
+
+    <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
new file mode 100644
index 0000000..7f5ed6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui;
+
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Manages notification when a guest session is resumed.
+ */
+public class GuestResumeSessionReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "GuestResumeSessionReceiver";
+
+    private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
+
+    private Dialog mNewSessionDialog;
+
+    public void register(Context context) {
+        IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+        context.registerReceiverAsUser(this, UserHandle.OWNER,
+                f, null /* permission */, null /* scheduler */);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+            cancelDialog();
+
+            int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            if (userId == UserHandle.USER_NULL) {
+                Log.e(TAG, intent + " sent to " + TAG + " without EXTRA_USER_HANDLE");
+                return;
+            }
+
+            UserInfo currentUser;
+            try {
+                currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+            } catch (RemoteException e) {
+                return;
+            }
+            if (!currentUser.isGuest()) {
+                return;
+            }
+
+            ContentResolver cr = context.getContentResolver();
+            int notFirstLogin = Settings.System.getIntForUser(
+                    cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
+            if (notFirstLogin != 0) {
+                mNewSessionDialog = new ResetSessionDialog(context, userId);
+                mNewSessionDialog.show();
+            } else {
+                Settings.System.putIntForUser(
+                        cr, SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
+            }
+        }
+    }
+
+    /**
+     * Wipes the guest session.
+     *
+     * The guest must be the current user and its id must be {@param userId}.
+     */
+    private static void wipeGuestSession(Context context, int userId) {
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        UserInfo currentUser;
+        try {
+            currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't wipe session because ActivityManager is dead");
+            return;
+        }
+        if (currentUser.id != userId) {
+            Log.w(TAG, "User requesting to start a new session (" + userId + ")"
+                    + " is not current user (" + currentUser.id + ")");
+            return;
+        }
+        if (!currentUser.isGuest()) {
+            Log.w(TAG, "User requesting to start a new session (" + userId + ")"
+                    + " is not a guest");
+            return;
+        }
+
+        userManager.removeUser(currentUser.id);
+        UserInfo newGuest = userManager.createGuest(context, currentUser.name);
+
+        try {
+            if (newGuest == null) {
+                Log.e(TAG, "Could not create new guest, switching back to owner");
+                ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
+                WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */);
+                return;
+            }
+            ActivityManagerNative.getDefault().switchUser(newGuest.id);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't wipe session because ActivityManager or WindowManager is dead");
+            return;
+        }
+    }
+
+    private void cancelDialog() {
+        if (mNewSessionDialog != null && mNewSessionDialog.isShowing()) {
+            mNewSessionDialog.cancel();
+            mNewSessionDialog = null;
+        }
+    }
+
+    private static class ResetSessionDialog extends SystemUIDialog implements
+            DialogInterface.OnClickListener {
+
+        private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
+        private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+
+        private final int mUserId;
+
+        public ResetSessionDialog(Context context, int userId) {
+            super(context);
+
+            setTitle(context.getString(R.string.guest_wipe_session_title));
+            setMessage(context.getString(R.string.guest_wipe_session_message));
+            setCanceledOnTouchOutside(false);
+
+            setButton(BUTTON_WIPE,
+                    context.getString(R.string.guest_wipe_session_wipe), this);
+            setButton(BUTTON_DONTWIPE,
+                    context.getString(R.string.guest_wipe_session_dontwipe), this);
+
+            mUserId = userId;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (which == BUTTON_WIPE) {
+                wipeGuestSession(getContext(), mUserId);
+                dismiss();
+            } else if (which == BUTTON_DONTWIPE) {
+                cancel();
+            }
+        }
+    }
+}
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 fb0f1c1..86bb202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -71,7 +71,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
@@ -1598,24 +1597,8 @@
         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.
+     * Hide the album artwork that is fading out and release its bitmap.
      */
     private Runnable mHideBackdropFront = new Runnable() {
         @Override
@@ -1624,7 +1607,8 @@
                 Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
             }
             mBackdropFront.setVisibility(View.INVISIBLE);
-            removeAndRecycleImageViewDrawable(mBackdropFront);
+            mBackdropFront.animate().cancel();
+            mBackdropFront.setImageDrawable(null);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
new file mode 100644
index 0000000..7f27a0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.WindowManager;
+
+/**
+ * Base class for dialogs that should appear over panels and keyguard.
+ */
+public class SystemUIDialog extends AlertDialog {
+
+    public SystemUIDialog(Context context) {
+        super(context, R.style.Theme_SystemUI_Dialog);
+
+        getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        WindowManager.LayoutParams attrs = getWindow().getAttributes();
+        attrs.setTitle("SystemUIDialog");
+        getWindow().setAttributes(attrs);
+    }
+}
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 2134042..49e7c67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -17,14 +17,18 @@
 package com.android.systemui.statusbar.policy;
 
 import com.android.systemui.BitmapHelper;
+import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.Dialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
@@ -52,15 +56,20 @@
 public class UserSwitcherController {
 
     private static final String TAG = "UserSwitcherController";
+    private static final boolean DEBUG = false;
 
     private final Context mContext;
     private final UserManager mUserManager;
     private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
+    private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
+            = new GuestResumeSessionReceiver();
 
     private ArrayList<UserRecord> mUsers = new ArrayList<>();
+    private Dialog mExitGuestDialog;
 
     public UserSwitcherController(Context context) {
         mContext = context;
+        mGuestResumeSessionReceiver.register(context);
         mUserManager = UserManager.get(context);
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_ADDED);
@@ -169,7 +178,7 @@
 
         if (ActivityManager.getCurrentUser() == id) {
             if (record.isGuest) {
-                exitGuest(id);
+                showExitGuestDialog(id);
             }
             return;
         }
@@ -186,8 +195,15 @@
         }
     }
 
+    private void showExitGuestDialog(int id) {
+        if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
+            mExitGuestDialog.cancel();
+        }
+        mExitGuestDialog = new ExitGuestDialog(mContext, id);
+        mExitGuestDialog.show();
+    }
+
     private void exitGuest(int id) {
-        // TODO: show confirmation dialog
         switchToUserId(UserHandle.USER_OWNER);
         mUserManager.removeUser(id);
     }
@@ -195,7 +211,16 @@
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (DEBUG) {
+                Log.v(TAG, "Broadcast: a=" + intent.getAction()
+                       + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+            }
             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+                if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
+                    mExitGuestDialog.cancel();
+                    mExitGuestDialog = null;
+                }
+
                 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 final int N = mUsers.size();
                 for (int i = 0; i < N; i++) {
@@ -341,4 +366,32 @@
         public void setToggleState(boolean state) {
         }
     };
+
+    private final class ExitGuestDialog extends SystemUIDialog implements
+            DialogInterface.OnClickListener {
+
+        private final int mGuestId;
+
+        public ExitGuestDialog(Context context, int guestId) {
+            super(context);
+            setTitle(R.string.guest_exit_guest_dialog_title);
+            setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
+            setButton(DialogInterface.BUTTON_NEGATIVE,
+                    context.getString(android.R.string.no), this);
+            setButton(DialogInterface.BUTTON_POSITIVE,
+                    context.getString(android.R.string.yes), this);
+            setCanceledOnTouchOutside(false);
+            mGuestId = guestId;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (which == BUTTON_NEGATIVE) {
+                cancel();
+            } else {
+                dismiss();
+                exitGuest(mGuestId);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9945909..ca9f6eb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -185,8 +185,8 @@
 public class ConnectivityService extends IConnectivityManager.Stub {
     private static final String TAG = "ConnectivityService";
 
-    private static final boolean DBG = true;
-    private static final boolean VDBG = true; // STOPSHIP
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false; // STOPSHIP
 
     // network sampling debugging
     private static final boolean SAMPLE_DBG = false;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 4740cae..45d3771 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -26,7 +26,6 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
-import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -199,9 +198,10 @@
         return mScreenAutoBrightness;
     }
 
-    public void updatePowerState(DisplayManagerInternal.DisplayPowerRequest request) {
-        if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
-                | setLightSensorEnabled(request.wantLightSensorEnabled())) {
+    public void configure(boolean enable, float adjustment) {
+        boolean changed = setLightSensorEnabled(enable);
+        changed |= setScreenAutoBrightnessAdjustment(adjustment);
+        if (changed) {
             updateAutoBrightness(false /*sendUpdate*/);
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a361e10..09221a3e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -213,9 +213,11 @@
     // The elapsed real time when the screen on was blocked.
     private long mScreenOnBlockStartRealTime;
 
-    // True if the screen auto-brightness value is actually being used to
-    // set the display brightness.
-    private boolean mUsingScreenAutoBrightness;
+    // Remembers whether certain kinds of brightness adjustments
+    // were recently applied so that we can decide how to transition.
+    private boolean mAppliedAutoBrightness;
+    private boolean mAppliedDimming;
+    private boolean mAppliedLowPower;
 
     // The controller for the automatic brightness level.
     private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -428,7 +430,6 @@
         // Update the power state request.
         final boolean mustNotify;
         boolean mustInitialize = false;
-        boolean wasDimOrDoze = false;
         boolean autoBrightnessAdjustmentChanged = false;
 
         synchronized (mLock) {
@@ -444,8 +445,6 @@
                 mPendingRequestChangedLocked = false;
                 mustInitialize = true;
             } else if (mPendingRequestChangedLocked) {
-                wasDimOrDoze = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM
-                        || mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE);
                 autoBrightnessAdjustmentChanged = (mPowerRequest.screenAutoBrightnessAdjustment
                         != mPendingRequestLocked.screenAutoBrightnessAdjustment);
                 mPowerRequest.copyFrom(mPendingRequestLocked);
@@ -463,10 +462,32 @@
             initialize();
         }
 
+        // Compute the basic display state using the policy.
+        // We might override this below based on other factors.
+        int state;
+        int brightness = PowerManager.BRIGHTNESS_DEFAULT;
+        switch (mPowerRequest.policy) {
+            case DisplayPowerRequest.POLICY_OFF:
+                state = Display.STATE_OFF;
+                break;
+            case DisplayPowerRequest.POLICY_DOZE:
+                if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
+                    state = mPowerRequest.dozeScreenState;
+                } else {
+                    state = Display.STATE_DOZE;
+                }
+                brightness = mPowerRequest.dozeScreenBrightness;
+                break;
+            case DisplayPowerRequest.POLICY_DIM:
+            case DisplayPowerRequest.POLICY_BRIGHT:
+            default:
+                state = Display.STATE_ON;
+                break;
+        }
+
         // Apply the proximity sensor.
         if (mProximitySensor != null) {
-            if (mPowerRequest.useProximitySensor
-                    && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+            if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
                 setProximitySensorEnabled(true);
                 if (!mScreenOffBecauseOfProximity
                         && mProximity == PROXIMITY_POSITIVE) {
@@ -476,7 +497,7 @@
             } else if (mWaitingForNegativeProximity
                     && mScreenOffBecauseOfProximity
                     && mProximity == PROXIMITY_POSITIVE
-                    && mPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+                    && state != Display.STATE_OFF) {
                 setProximitySensorEnabled(true);
             } else {
                 setProximitySensorEnabled(false);
@@ -490,63 +511,89 @@
         } else {
             mWaitingForNegativeProximity = false;
         }
-
-        // Turn on the light sensor if needed.
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.updatePowerState(mPowerRequest);
+        if (mScreenOffBecauseOfProximity) {
+            state = Display.STATE_OFF;
         }
 
-        // Set the screen brightness.
-        if (mPowerRequest.wantScreenOnAny()) {
-            int target;
-            boolean slow;
-            int screenAutoBrightness = mAutomaticBrightnessController != null ?
-                    mAutomaticBrightnessController.getAutomaticScreenBrightness() : -1;
-            if (screenAutoBrightness >= 0 && mPowerRequest.useAutoBrightness) {
-                // Use current auto-brightness value.
-                target = screenAutoBrightness;
-                slow = mUsingScreenAutoBrightness && !autoBrightnessAdjustmentChanged;
-                mUsingScreenAutoBrightness = true;
+        // Use zero brightness when screen is off.
+        if (state == Display.STATE_OFF) {
+            brightness = PowerManager.BRIGHTNESS_OFF;
+        }
+
+        // Use default brightness when dozing unless overridden.
+        if (brightness < 0 && (state == Display.STATE_DOZE
+                || state == Display.STATE_DOZE_SUSPEND)) {
+            brightness = mScreenBrightnessDozeConfig;
+        }
+
+        // Configure auto-brightness.
+        boolean autoBrightnessEnabled = false;
+        if (mAutomaticBrightnessController != null) {
+            autoBrightnessEnabled = mPowerRequest.useAutoBrightness
+                    && state == Display.STATE_ON && brightness < 0;
+            mAutomaticBrightnessController.configure(autoBrightnessEnabled,
+                    mPowerRequest.screenAutoBrightnessAdjustment);
+        }
+
+        // Apply auto-brightness.
+        boolean slowChange = false;
+        if (brightness < 0) {
+            if (autoBrightnessEnabled) {
+                brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();
+            }
+            if (brightness >= 0) {
+                // Use current auto-brightness value and slowly adjust to changes.
+                brightness = clampScreenBrightness(brightness);
+                if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
+                    slowChange = true; // slowly adapt to auto-brightness
+                }
+                mAppliedAutoBrightness = true;
             } else {
-                // Light sensor is disabled or not ready yet.
-                // Use the current brightness setting from the request, which is expected
-                // provide a nominal default value for the case where auto-brightness
-                // is not ready yet.
-                target = mPowerRequest.screenBrightness;
-                slow = false;
-                mUsingScreenAutoBrightness = false;
+                mAppliedAutoBrightness = false;
             }
-            if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE) {
-                // Dim quickly to the doze state.
-                target = mScreenBrightnessDozeConfig;
-                slow = false;
-            } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
-                // Dim quickly by at least some minimum amount.
-                target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION,
-                        mScreenBrightnessDimConfig);
-                slow = false;
-            } else if (wasDimOrDoze) {
-                // Brighten quickly.
-                slow = false;
-            }
-            // If low power mode is enabled, brightness level
-            // would be scaled down to half
-            if (mPowerRequest.lowPowerMode) {
-                target = target/2;
-            }
-            animateScreenBrightness(clampScreenBrightness(target),
-                    slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
         } else {
-            // Screen is off.  Don't bother changing the brightness.
-            mUsingScreenAutoBrightness = false;
+            mAppliedAutoBrightness = false;
+        }
+
+        // Apply manual brightness.
+        // Use the current brightness setting from the request, which is expected
+        // provide a nominal default value for the case where auto-brightness
+        // is not ready yet.
+        if (brightness < 0) {
+            brightness = clampScreenBrightness(mPowerRequest.screenBrightness);
+        }
+
+        // Apply dimming by at least some minimum amount when user activity
+        // timeout is about to expire.
+        if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+            brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,
+                    mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
+            if (!mAppliedDimming) {
+                slowChange = false;
+            }
+            mAppliedDimming = true;
+        }
+
+        // If low power mode is enabled, cut the brightness level by half
+        // as long as it is above the minimum threshold.
+        if (mPowerRequest.lowPowerMode) {
+            if (brightness > mScreenBrightnessRangeMinimum) {
+                brightness = Math.max(brightness / 2, mScreenBrightnessRangeMinimum);
+            }
+            if (!mAppliedLowPower) {
+                slowChange = false;
+            }
+            mAppliedLowPower = true;
+        }
+
+        // Animate the screen brightness when the screen is on.
+        if (state != Display.STATE_OFF) {
+            animateScreenBrightness(brightness, slowChange
+                    ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
         }
 
         // Animate the screen on or off unless blocked.
-        if (mScreenOffBecauseOfProximity) {
-            // Screen off due to proximity.
-            setScreenState(Display.STATE_OFF);
-            unblockScreenOn();
-        } else if (mPowerRequest.wantScreenOnAny()) {
+        if (state == Display.STATE_ON) {
             // Want screen on.
             // Wait for previous off animation to complete beforehand.
             // It is relatively short but if we cancel it and switch to the
@@ -555,21 +602,14 @@
                 // Turn the screen on.  The contents of the screen may not yet
                 // be visible if the electron beam has not been dismissed because
                 // its last frame of animation is solid black.
-
-                if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE) {
-                    if (!mScreenBrightnessRampAnimator.isAnimating()) {
-                        setScreenState(Display.STATE_DOZE);
-                    }
-                } else {
-                    setScreenState(Display.STATE_ON);
-                }
-
+                setScreenState(Display.STATE_ON);
                 if (mPowerRequest.blockScreenOn
                         && mPowerState.getElectronBeamLevel() == 0.0f) {
                     blockScreenOn();
                 } else {
                     unblockScreenOn();
-                    if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+                    if (USE_ELECTRON_BEAM_ON_ANIMATION && mPowerRequest.isBrightOrDim()) {
+                        // Perform screen on animation.
                         if (!mElectronBeamOnAnimator.isStarted()) {
                             if (mPowerState.getElectronBeamLevel() == 1.0f) {
                                 mPowerState.dismissElectronBeam();
@@ -583,28 +623,59 @@
                             }
                         }
                     } else {
+                        // Skip screen on animation.
                         mPowerState.setElectronBeamLevel(1.0f);
                         mPowerState.dismissElectronBeam();
                     }
                 }
             }
+        } else if (state == Display.STATE_DOZE) {
+            // Want screen dozing.
+            // Wait for brightness animation to complete beforehand when entering doze
+            // from screen on.
+            unblockScreenOn();
+            if (!mScreenBrightnessRampAnimator.isAnimating()
+                    || mPowerState.getScreenState() != Display.STATE_ON) {
+                // Set screen state and dismiss the black surface without fanfare.
+                setScreenState(state);
+                mPowerState.setElectronBeamLevel(1.0f);
+                mPowerState.dismissElectronBeam();
+            }
+        } else if (state == Display.STATE_DOZE_SUSPEND) {
+            // Want screen dozing and suspended.
+            // Wait for brightness animation to complete beforehand unless already
+            // suspended because we may not be able to change it after suspension.
+            unblockScreenOn();
+            if (!mScreenBrightnessRampAnimator.isAnimating()
+                    || mPowerState.getScreenState() == Display.STATE_DOZE_SUSPEND) {
+                // Set screen state and dismiss the black surface without fanfare.
+                setScreenState(state);
+                mPowerState.setElectronBeamLevel(1.0f);
+                mPowerState.dismissElectronBeam();
+            }
         } else {
             // Want screen off.
             // Wait for previous on animation to complete beforehand.
             unblockScreenOn();
             if (!mElectronBeamOnAnimator.isStarted()) {
-                if (!mElectronBeamOffAnimator.isStarted()) {
-                    if (mPowerState.getElectronBeamLevel() == 0.0f) {
-                        setScreenState(Display.STATE_OFF);
-                    } else if (mPowerState.prepareElectronBeam(
-                            mElectronBeamFadesConfig ?
-                                    ElectronBeam.MODE_FADE :
-                                            ElectronBeam.MODE_COOL_DOWN)
-                            && mPowerState.getScreenState() != Display.STATE_OFF) {
-                        mElectronBeamOffAnimator.start();
-                    } else {
-                        mElectronBeamOffAnimator.end();
+                if (mPowerRequest.policy == DisplayPowerRequest.POLICY_OFF) {
+                    // Perform screen off animation.
+                    if (!mElectronBeamOffAnimator.isStarted()) {
+                        if (mPowerState.getElectronBeamLevel() == 0.0f) {
+                            setScreenState(Display.STATE_OFF);
+                        } else if (mPowerState.prepareElectronBeam(
+                                mElectronBeamFadesConfig ?
+                                        ElectronBeam.MODE_FADE :
+                                                ElectronBeam.MODE_COOL_DOWN)
+                                && mPowerState.getScreenState() != Display.STATE_OFF) {
+                            mElectronBeamOffAnimator.start();
+                        } else {
+                            mElectronBeamOffAnimator.end();
+                        }
                     }
+                } else {
+                    // Skip screen off animation.
+                    setScreenState(Display.STATE_OFF);
                 }
             }
         }
@@ -856,7 +927,9 @@
         pw.println("  mPendingProximityDebounceTime="
                 + TimeUtils.formatUptime(mPendingProximityDebounceTime));
         pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
-        pw.println("  mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
+        pw.println("  mAppliedAutoBrightness=" + mAppliedAutoBrightness);
+        pw.println("  mAppliedDimming=" + mAppliedDimming);
+        pw.println("  mAppliedLowPower=" + mAppliedLowPower);
 
         pw.println("  mScreenBrightnessRampAnimator.isAnimating()=" +
                 mScreenBrightnessRampAnimator.isAnimating());
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index a5f8849..4821e74 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -402,13 +402,14 @@
                         Slog.d(TAG, "Updating screen state: state="
                                 + Display.stateToString(state) + ", backlight=" + backlight);
                     }
-                    if (stateChanged && state != Display.STATE_OFF) {
+                    boolean suspending = Display.isSuspendedState(state);
+                    if (stateChanged && !suspending) {
                         mBlanker.requestDisplayState(state);
                     }
                     if (backlightChanged) {
                         mBacklight.setBrightness(backlight);
                     }
-                    if (stateChanged && state == Display.STATE_OFF) {
+                    if (stateChanged && suspending) {
                         mBlanker.requestDisplayState(state);
                     }
                 }
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 4ccf73b..107a6f6 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -38,6 +38,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -48,6 +49,7 @@
 import android.service.dreams.IDreamManager;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.view.Display;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -71,6 +73,7 @@
     private final DreamHandler mHandler;
     private final DreamController mController;
     private final PowerManager mPowerManager;
+    private final PowerManagerInternal mPowerManagerInternal;
     private final PowerManager.WakeLock mDozeWakeLock;
     private final McuHal mMcuHal; // synchronized on self
 
@@ -81,6 +84,8 @@
     private boolean mCurrentDreamCanDoze;
     private boolean mCurrentDreamIsDozing;
     private boolean mCurrentDreamIsWaking;
+    private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
+    private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
     private DozeHardwareWrapper mCurrentDreamDozeHardware;
 
     public DreamManagerService(Context context) {
@@ -90,6 +95,7 @@
         mController = new DreamController(context, mHandler, mControllerListener);
 
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
         mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
 
         mMcuHal = McuHal.open();
@@ -134,6 +140,9 @@
         pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
         pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
         pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
+        pw.println("mCurrentDreamDozeScreenState="
+                + Display.stateToString(mCurrentDreamDozeScreenState));
+        pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
         pw.println("mCurrentDreamDozeHardware=" + mCurrentDreamDozeHardware);
         pw.println("getDozeComponent()=" + getDozeComponent());
         pw.println();
@@ -213,16 +222,24 @@
         }
     }
 
-    private void startDozingInternal(IBinder token) {
+    private void startDozingInternal(IBinder token, int screenState,
+            int screenBrightness) {
         if (DEBUG) {
-            Slog.d(TAG, "Dream requested to start dozing: " + token);
+            Slog.d(TAG, "Dream requested to start dozing: " + token
+                    + ", screenState=" + screenState
+                    + ", screenBrightness=" + screenBrightness);
         }
 
         synchronized (mLock) {
-            if (mCurrentDreamToken == token && mCurrentDreamCanDoze
-                    && !mCurrentDreamIsDozing) {
-                mCurrentDreamIsDozing = true;
-                mDozeWakeLock.acquire();
+            if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
+                mCurrentDreamDozeScreenState = screenState;
+                mCurrentDreamDozeScreenBrightness = screenBrightness;
+                mPowerManagerInternal.setDozeOverrideFromDreamManager(
+                        screenState, screenBrightness);
+                if (!mCurrentDreamIsDozing) {
+                    mCurrentDreamIsDozing = true;
+                    mDozeWakeLock.acquire();
+                }
             }
         }
     }
@@ -236,6 +253,8 @@
             if (mCurrentDreamToken == token && mCurrentDreamIsDozing) {
                 mCurrentDreamIsDozing = false;
                 mDozeWakeLock.release();
+                mPowerManagerInternal.setDozeOverrideFromDreamManager(
+                        Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
             }
         }
     }
@@ -399,6 +418,8 @@
             mCurrentDreamIsDozing = false;
             mDozeWakeLock.release();
         }
+        mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
+        mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
         if (mCurrentDreamDozeHardware != null) {
             mCurrentDreamDozeHardware.release();
             mCurrentDreamDozeHardware = null;
@@ -593,7 +614,7 @@
         }
 
         @Override // Binder call
-        public void startDozing(IBinder token) {
+        public void startDozing(IBinder token, int screenState, int screenBrightness) {
             // Requires no permission, called by Dream from an arbitrary process.
             if (token == null) {
                 throw new IllegalArgumentException("token must not be null");
@@ -601,7 +622,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                startDozingInternal(token);
+                startDozingInternal(token, screenState, screenBrightness);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 85c7747..946d4ce 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -16,6 +16,8 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+
 /**
  * Defines constants related to HDMI-CEC protocol internal implementation.
  * If a constant will be used in the public api, it should be located in
@@ -78,7 +80,7 @@
     public static final int ADDR_INVALID = -1;
 
     /** Logical address used to indicate the source comes from internal device. */
-    public static final int ADDR_INTERNAL = 0xFFFF;
+    public static final int ADDR_INTERNAL = HdmiCecDeviceInfo.ADDR_INTERNAL;
 
     static final int MESSAGE_FEATURE_ABORT = 0x00;
     static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
@@ -179,10 +181,11 @@
     static final int INVALID_PORT_ID = -1;
     static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
 
-    // Send result codes.
+    // Send result codes. It should be consistent with hdmi_cec.h's send_message error code.
     static final int SEND_RESULT_SUCCESS = 0;
-    static final int SEND_RESULT_NAK = -1;
-    static final int SEND_RESULT_FAILURE = -2;
+    static final int SEND_RESULT_NAK = 1;
+    static final int SEND_RESULT_BUSY = 2;
+    static final int SEND_RESULT_FAILURE = 3;
 
     // Strategy for device polling.
     // Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index a66f473..e985e35 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -556,6 +556,9 @@
     @ServiceThreadOnly
     private void clearDeviceInfoList() {
         assertRunOnServiceThread();
+        for (HdmiCecDeviceInfo info : mSafeExternalInputs) {
+            mService.invokeDeviceEventListeners(info, false);
+        }
         mDeviceInfos.clear();
         updateSafeDeviceInfoList();
     }
@@ -1129,6 +1132,7 @@
 
         disableSystemAudioIfExist();
         disableArcIfExist();
+        clearDeviceInfoList();
         checkIfPendingActionsCleared();
     }
 
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index b3419c1..09873c7 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -322,7 +322,7 @@
     private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() {
         @Override
         protected boolean isSupported() {
-            return native_is_measurement_supported();
+            return GpsLocationProvider.isSupported();
         }
     };
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f7cb346..f69c066 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2366,6 +2366,9 @@
         try {
             ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
                     pkg, 0, UserHandle.getCallingUserId());
+            if (ai == null) {
+                throw new SecurityException("Unknown package " + pkg);
+            }
             if (!UserHandle.isSameApp(ai.uid, uid)) {
                 throw new SecurityException("Calling uid " + uid + " gave package"
                         + pkg + " which is owned by uid " + ai.uid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 77fd409..69f2f32 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -474,6 +474,9 @@
     final SparseArray<PackageVerificationState> mPendingVerification
             = new SparseArray<PackageVerificationState>();
 
+    /** Set of packages associated with each app op permission. */
+    final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
     final PackageInstallerService mInstallerService;
 
     HashSet<PackageParser.Package> mDeferredDexOpt = null;
@@ -2917,6 +2920,17 @@
     }
 
     @Override
+    public String[] getAppOpPermissionPackages(String permissionName) {
+        synchronized (mPackages) {
+            ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
+            if (pkgs == null) {
+                return null;
+            }
+            return pkgs.toArray(new String[pkgs.size()]);
+        }
+    }
+
+    @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
@@ -6591,6 +6605,31 @@
                     r.append(p.info.name);
                 }
             }
+            if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+                ArraySet<String> appOpPerms = mAppOpPermissionPackages.get(p.info.name);
+                if (appOpPerms != null) {
+                    appOpPerms.remove(pkg.packageName);
+                }
+            }
+        }
+        if (r != null) {
+            if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
+        }
+
+        N = pkg.requestedPermissions.size();
+        r = null;
+        for (i=0; i<N; i++) {
+            String perm = pkg.requestedPermissions.get(i);
+            BasePermission bp = mSettings.mPermissions.get(perm);
+            if (bp != null && (bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+                ArraySet<String> appOpPerms = mAppOpPermissionPackages.get(perm);
+                if (appOpPerms != null) {
+                    appOpPerms.remove(pkg.packageName);
+                    if (appOpPerms.isEmpty()) {
+                        mAppOpPermissionPackages.remove(perm);
+                    }
+                }
+            }
         }
         if (r != null) {
             if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
@@ -6775,6 +6814,15 @@
             final String perm = bp.name;
             boolean allowed;
             boolean allowedSig = false;
+            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+                // Keep track of app op permissions.
+                ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
+                if (pkgs == null) {
+                    pkgs = new ArraySet<>();
+                    mAppOpPermissionPackages.put(bp.name, pkgs);
+                }
+                pkgs.add(pkg.packageName);
+            }
             final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
             if (level == PermissionInfo.PROTECTION_NORMAL
                     || level == PermissionInfo.PROTECTION_DANGEROUS) {
@@ -6837,7 +6885,9 @@
                             + " (protectionLevel=" + bp.protectionLevel
                             + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                             + ")");
-                } else {
+                } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
+                    // Don't print warning for app op permissions, since it is fine for them
+                    // not to be granted, there is a UI for the user to decide.
                     Slog.w(TAG, "Not granting permission " + perm
                             + " to package " + pkg.packageName
                             + " (protectionLevel=" + bp.protectionLevel
@@ -7882,7 +7932,7 @@
     }
 
     @Override
-    public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+    public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
             int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         PackageSetting pkgSetting;
@@ -7890,11 +7940,11 @@
         if (UserHandle.getUserId(uid) != userId) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "setApplicationBlockedSetting for user " + userId);
+                    "setApplicationHiddenSetting for user " + userId);
         }
 
-        if (blocked && isPackageDeviceAdmin(packageName, userId)) {
-            Slog.w(TAG, "Not blocking package " + packageName + ": has active device admin");
+        if (hidden && isPackageDeviceAdmin(packageName, userId)) {
+            Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
             return false;
         }
 
@@ -7908,10 +7958,10 @@
                 if (pkgSetting == null) {
                     return false;
                 }
-                if (pkgSetting.getBlocked(userId) != blocked) {
-                    pkgSetting.setBlocked(blocked, userId);
+                if (pkgSetting.getHidden(userId) != hidden) {
+                    pkgSetting.setHidden(hidden, userId);
                     mSettings.writePackageRestrictionsLPr(userId);
-                    if (blocked) {
+                    if (hidden) {
                         sendRemoved = true;
                     } else {
                         sendAdded = true;
@@ -7924,8 +7974,8 @@
             }
             if (sendRemoved) {
                 killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId),
-                        "blocking pkg");
-                sendPackageBlockedForUser(packageName, pkgSetting, userId);
+                        "hiding pkg");
+                sendApplicationHiddenForUser(packageName, pkgSetting, userId);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -7933,7 +7983,7 @@
         return false;
     }
 
-    private void sendPackageBlockedForUser(String packageName, PackageSetting pkgSetting,
+    private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
             int userId) {
         final PackageRemovedInfo info = new PackageRemovedInfo();
         info.removedPackage = packageName;
@@ -7944,13 +7994,13 @@
 
     /**
      * Returns true if application is not found or there was an error. Otherwise it returns
-     * the blocked state of the package for the given user.
+     * the hidden state of the package for the given user.
      */
     @Override
-    public boolean getApplicationBlockedSettingAsUser(String packageName, int userId) {
+    public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true,
-                "getApplicationBlocked for user " + userId);
+                "getApplicationHidden for user " + userId);
         PackageSetting pkgSetting;
         long callingId = Binder.clearCallingIdentity();
         try {
@@ -7960,7 +8010,7 @@
                 if (pkgSetting == null) {
                     return true;
                 }
-                return pkgSetting.getBlocked(userId);
+                return pkgSetting.getHidden(userId);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -7994,7 +8044,7 @@
                 }
                 if (!pkgSetting.getInstalled(userId)) {
                     pkgSetting.setInstalled(true, userId);
-                    pkgSetting.setBlocked(false, userId);
+                    pkgSetting.setHidden(false, userId);
                     mSettings.writePackageRestrictionsLPr(userId);
                     sendAdded = true;
                 }
@@ -10593,19 +10643,19 @@
             return;
         }
 
-        boolean blocked = false;
+        boolean uninstallBlocked = false;
         if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
             int[] users = sUserManager.getUserIds();
             for (int i = 0; i < users.length; ++i) {
                 if (getBlockUninstallForUser(packageName, users[i])) {
-                    blocked = true;
+                    uninstallBlocked = true;
                     break;
                 }
             }
         } else {
-            blocked = getBlockUninstallForUser(packageName, userId);
+            uninstallBlocked = getBlockUninstallForUser(packageName, userId);
         }
-        if (blocked) {
+        if (uninstallBlocked) {
             try {
                 observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED);
             } catch (RemoteException re) {
@@ -10613,7 +10663,9 @@
             return;
         }
 
-        if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
+        if (DEBUG_REMOVE) {
+            Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
+        }
         // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(new Runnable() {
             public void run() {
@@ -11032,7 +11084,7 @@
                         false, //installed
                         true,  //stopped
                         true,  //notLaunched
-                        false, //blocked
+                        false, //hidden
                         null, null, null,
                         false // blockUninstall
                         );
@@ -12424,6 +12476,22 @@
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
                 mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
+                if (packageName == null) {
+                    for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
+                        if (iperm == 0) {
+                            if (dumpState.onTitlePrinted())
+                                pw.println();
+                            pw.println("AppOp Permissions:");
+                        }
+                        pw.print("  AppOp Permission ");
+                        pw.print(mAppOpPermissionPackages.keyAt(iperm));
+                        pw.println(":");
+                        ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
+                        for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
+                            pw.print("    "); pw.println(pkgs.valueAt(ipkg));
+                        }
+                    }
+                }
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index e164f5f..e29332c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -281,12 +281,12 @@
         modifyUserState(userId).notLaunched = stop;
     }
 
-    boolean getBlocked(int userId) {
-        return readUserState(userId).blocked;
+    boolean getHidden(int userId) {
+        return readUserState(userId).hidden;
     }
 
-    void setBlocked(boolean blocked, int userId) {
-        modifyUserState(userId).blocked = blocked;
+    void setHidden(boolean hidden, int userId) {
+        modifyUserState(userId).hidden = hidden;
     }
 
     boolean getBlockUninstall(int userId) {
@@ -298,7 +298,7 @@
     }
 
     void setUserState(int userId, int enabled, boolean installed, boolean stopped,
-            boolean notLaunched, boolean blocked,
+            boolean notLaunched, boolean hidden,
             String lastDisableAppCaller, HashSet<String> enabledComponents,
             HashSet<String> disabledComponents, boolean blockUninstall) {
         PackageUserState state = modifyUserState(userId);
@@ -306,7 +306,7 @@
         state.installed = installed;
         state.stopped = stopped;
         state.notLaunched = notLaunched;
-        state.blocked = blocked;
+        state.hidden = hidden;
         state.lastDisableAppCaller = lastDisableAppCaller;
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e16894e..d1ea3e1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -153,7 +153,10 @@
     private static final String ATTR_ENABLED = "enabled";
     private static final String ATTR_ENABLED_CALLER = "enabledCaller";
     private static final String ATTR_STOPPED = "stopped";
+    // Legacy, here for reading older versions of the package-restrictions.
     private static final String ATTR_BLOCKED = "blocked";
+    // New name for the above attribute.
+    private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
 
@@ -606,7 +609,7 @@
                                     installed,
                                     true, // stopped,
                                     true, // notLaunched
-                                    false, // blocked
+                                    false, // hidden
                                     null, null, null,
                                     false // blockUninstall
                                     );
@@ -1177,7 +1180,7 @@
                                 true,   // installed
                                 false,  // stopped
                                 false,  // notLaunched
-                                false,  // blocked
+                                false,  // hidden
                                 null, null, null,
                                 false // blockUninstall
                                 );
@@ -1233,9 +1236,14 @@
                     final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
                     final boolean stopped = stoppedStr == null
                             ? false : Boolean.parseBoolean(stoppedStr);
+                    // For backwards compatibility with the previous name of "blocked", which
+                    // now means hidden, read the old attribute as well.
                     final String blockedStr = parser.getAttributeValue(null, ATTR_BLOCKED);
-                    final boolean blocked = blockedStr == null
+                    boolean hidden = blockedStr == null
                             ? false : Boolean.parseBoolean(blockedStr);
+                    final String hiddenStr = parser.getAttributeValue(null, ATTR_HIDDEN);
+                    hidden = hiddenStr == null
+                            ? hidden : Boolean.parseBoolean(hiddenStr);
                     final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
                     final boolean notLaunched = stoppedStr == null
                             ? false : Boolean.parseBoolean(notLaunchedStr);
@@ -1263,7 +1271,7 @@
                         }
                     }
 
-                    ps.setUserState(userId, enabled, installed, stopped, notLaunched, blocked,
+                    ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
                             enabledCaller, enabledComponents, disabledComponents, blockUninstall);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
@@ -1432,7 +1440,7 @@
                 PackageUserState ustate = pkg.readUserState(userId);
                 if (ustate.stopped || ustate.notLaunched || !ustate.installed
                         || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
-                        || ustate.blocked
+                        || ustate.hidden
                         || (ustate.enabledComponents != null
                                 && ustate.enabledComponents.size() > 0)
                         || (ustate.disabledComponents != null
@@ -1451,8 +1459,8 @@
                     if (ustate.notLaunched) {
                         serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
                     }
-                    if (ustate.blocked) {
-                        serializer.attribute(null, ATTR_BLOCKED, "true");
+                    if (ustate.hidden) {
+                        serializer.attribute(null, ATTR_HIDDEN, "true");
                     }
                     if (ustate.blockUninstall) {
                         serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
@@ -3443,7 +3451,7 @@
                 pw.print(user.id);
                 pw.print(",");
                 pw.print(ps.getInstalled(user.id) ? "I" : "i");
-                pw.print(ps.getBlocked(user.id) ? "B" : "b");
+                pw.print(ps.getHidden(user.id) ? "B" : "b");
                 pw.print(ps.getStopped(user.id) ? "S" : "s");
                 pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
                 pw.print(",");
@@ -3583,8 +3591,8 @@
             pw.print(prefix); pw.print("  User "); pw.print(user.id); pw.print(": ");
             pw.print(" installed=");
             pw.print(ps.getInstalled(user.id));
-            pw.print(" blocked=");
-            pw.print(ps.getBlocked(user.id));
+            pw.print(" hidden=");
+            pw.print(ps.getHidden(user.id));
             pw.print(" stopped=");
             pw.print(ps.getStopped(user.id));
             pw.print(" notLaunched=");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b4cc252..33ecc4d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1428,7 +1428,7 @@
         removeRestrictionsForUser(userHandle, true);
     }
 
-    private void removeRestrictionsForUser(final int userHandle, boolean unblockApps) {
+    private void removeRestrictionsForUser(final int userHandle, boolean unhideApps) {
         synchronized (mPackagesLock) {
             // Remove all user restrictions
             setUserRestrictions(new Bundle(), userHandle);
@@ -1437,12 +1437,12 @@
             // Remove any app restrictions
             cleanAppRestrictions(userHandle);
         }
-        if (unblockApps) {
-            unblockAllAppsForUser(userHandle);
+        if (unhideApps) {
+            unhideAllInstalledAppsForUser(userHandle);
         }
     }
 
-    private void unblockAllAppsForUser(final int userHandle) {
+    private void unhideAllInstalledAppsForUser(final int userHandle) {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -1453,8 +1453,8 @@
                 try {
                     for (ApplicationInfo appInfo : apps) {
                         if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0
-                                && (appInfo.flags & ApplicationInfo.FLAG_BLOCKED) != 0) {
-                            mPm.setApplicationBlockedSettingAsUser(appInfo.packageName, false,
+                                && (appInfo.flags & ApplicationInfo.FLAG_HIDDEN) != 0) {
+                            mPm.setApplicationHiddenSettingAsUser(appInfo.packageName, false,
                                     userHandle);
                         }
                     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4b1e8eb..8c52fad 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -404,6 +404,12 @@
     // Use NaN to disable.
     private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
 
+    // The screen state to use while dozing.
+    private int mDozeScreenStateOverrideFromDreamManager = Display.STATE_UNKNOWN;
+
+    // The screen brightness to use while dozing.
+    private int mDozeScreenBrightnessOverrideFromDreamManager = PowerManager.BRIGHTNESS_DEFAULT;
+
     // Time when we last logged a warning about calling userActivity() without permission.
     private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
 
@@ -1370,11 +1376,12 @@
                 if (mUserActivitySummary == 0
                         && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
                     nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
-                    if (now < nextTimeout
-                            && mDisplayPowerRequest.wantScreenOnNormal()) {
-                        mUserActivitySummary = mDisplayPowerRequest.screenState
-                                == DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
-                                USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
+                    if (now < nextTimeout) {
+                        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) {
+                            mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+                        } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+                        }
                     }
                 }
                 if (mUserActivitySummary != 0) {
@@ -1631,7 +1638,7 @@
         if (mWakefulness != WAKEFULNESS_DREAMING
                 || !mDreamsSupportedConfig
                 || !mDreamsEnabledSetting
-                || !mDisplayPowerRequest.wantScreenOnNormal()
+                || !mDisplayPowerRequest.isBrightOrDim()
                 || !mBootCompleted) {
             return false;
         }
@@ -1672,8 +1679,7 @@
         if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                 | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                 | DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) {
-            final int newScreenState = getDesiredScreenPowerStateLocked();
-            mDisplayPowerRequest.screenState = newScreenState;
+            mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
 
             int screenBrightness = mScreenBrightnessSettingDefault;
             float screenAutoBrightnessAdjustment = 0.0f;
@@ -1713,13 +1719,22 @@
 
             mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
 
+            if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
+                mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+                mDisplayPowerRequest.dozeScreenBrightness =
+                        mDozeScreenBrightnessOverrideFromDreamManager;
+            } else {
+                mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
+                mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+            }
+
             mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                     mRequestWaitForNegativeProximity);
             mRequestWaitForNegativeProximity = false;
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady
-                        + ", newScreenState=" + newScreenState
+                        + ", policy=" + mDisplayPowerRequest.policy
                         + ", mWakefulness=" + mWakefulness
                         + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
                         + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
@@ -1737,22 +1752,22 @@
         return value >= -1.0f && value <= 1.0f;
     }
 
-    private int getDesiredScreenPowerStateLocked() {
+    private int getDesiredScreenPolicyLocked() {
         if (mWakefulness == WAKEFULNESS_ASLEEP) {
-            return DisplayPowerRequest.SCREEN_STATE_OFF;
+            return DisplayPowerRequest.POLICY_OFF;
         }
 
         if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
-            return DisplayPowerRequest.SCREEN_STATE_DOZE;
+            return DisplayPowerRequest.POLICY_DOZE;
         }
 
         if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
                 || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                 || !mBootCompleted) {
-            return DisplayPowerRequest.SCREEN_STATE_BRIGHT;
+            return DisplayPowerRequest.POLICY_BRIGHT;
         }
 
-        return DisplayPowerRequest.SCREEN_STATE_DIM;
+        return DisplayPowerRequest.POLICY_DIM;
     }
 
     private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
@@ -1895,7 +1910,7 @@
         if (!mDisplayReady) {
             return true;
         }
-        if (mDisplayPowerRequest.wantScreenOnNormal()) {
+        if (mDisplayPowerRequest.isBrightOrDim()) {
             // If we asked for the screen to be on but it is off due to the proximity
             // sensor then we may suspend but only if the configuration allows it.
             // On some hardware it may not be safe to suspend because the proximity
@@ -2103,6 +2118,19 @@
         }
     }
 
+    private void setDozeOverrideFromDreamManagerInternal(
+            int screenState, int screenBrightness) {
+        synchronized (mLock) {
+            if (mDozeScreenStateOverrideFromDreamManager != screenState
+                    || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightness) {
+                mDozeScreenStateOverrideFromDreamManager = screenState;
+                mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness;
+                mDirty |= DIRTY_SETTINGS;
+                updatePowerStateLocked();
+            }
+        }
+    }
+
     private void powerHintInternal(int hintId, int data) {
         nativeSendPowerHint(hintId, data);
     }
@@ -2257,6 +2285,10 @@
                     + mTemporaryScreenBrightnessSettingOverride);
             pw.println("  mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
                     + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
+            pw.println("  mDozeScreenStateOverrideFromDreamManager="
+                    + mDozeScreenStateOverrideFromDreamManager);
+            pw.println("  mDozeScreenBrightnessOverrideFromDreamManager="
+                    + mDozeScreenBrightnessOverrideFromDreamManager);
             pw.println("  mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
             pw.println("  mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
             pw.println("  mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
@@ -3026,63 +3058,44 @@
     }
 
     private final class LocalService extends PowerManagerInternal {
-        /**
-         * Used by the window manager to override the screen brightness based on the
-         * current foreground activity.
-         *
-         * This method must only be called by the window manager.
-         *
-         * @param brightness The overridden brightness, or -1 to disable the override.
-         */
         @Override
-        public void setScreenBrightnessOverrideFromWindowManager(int brightness) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.DEVICE_POWER, null);
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                setScreenBrightnessOverrideFromWindowManagerInternal(brightness);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+        public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {
+            if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT
+                    || screenBrightness > PowerManager.BRIGHTNESS_ON) {
+                screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
             }
+            setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness);
         }
 
-        /**
-         * Used by the window manager to override the button brightness based on the
-         * current foreground activity.
-         *
-         * This method must only be called by the window manager.
-         *
-         * @param brightness The overridden brightness, or -1 to disable the override.
-         */
         @Override
-        public void setButtonBrightnessOverrideFromWindowManager(int brightness) {
+        public void setButtonBrightnessOverrideFromWindowManager(int screenBrightness) {
             // Do nothing.
             // Button lights are not currently supported in the new implementation.
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.DEVICE_POWER, null);
         }
 
-        /**
-         * Used by the window manager to override the user activity timeout based on the
-         * current foreground activity.  It can only be used to make the timeout shorter
-         * than usual, not longer.
-         *
-         * This method must only be called by the window manager.
-         *
-         * @param timeoutMillis The overridden timeout, or -1 to disable the override.
-         */
+        @Override
+        public void setDozeOverrideFromDreamManager(int screenState, int screenBrightness) {
+            switch (screenState) {
+                case Display.STATE_UNKNOWN:
+                case Display.STATE_OFF:
+                case Display.STATE_DOZE:
+                case Display.STATE_DOZE_SUSPEND:
+                case Display.STATE_ON:
+                    break;
+                default:
+                    screenState = Display.STATE_UNKNOWN;
+                    break;
+            }
+            if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT
+                    || screenBrightness > PowerManager.BRIGHTNESS_ON) {
+                screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+            }
+            setDozeOverrideFromDreamManagerInternal(screenState, screenBrightness);
+        }
+
         @Override
         public void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.DEVICE_POWER, null);
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 0a9c646..ea19012 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -211,9 +211,6 @@
     }
 
     public void addHardwareTvInput(int deviceId, TvInputInfo info) {
-        if (info.getType() == TvInputInfo.TYPE_VIRTUAL) {
-            throw new IllegalArgumentException("info (" + info + ") has virtual type.");
-        }
         synchronized (mLock) {
             String oldInputId = mHardwareInputIdMap.get(deviceId);
             if (oldInputId != null) {
@@ -235,14 +232,23 @@
         }
     }
 
+    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
+        for (int i = 0; i < map.size(); ++i) {
+            if (map.valueAt(i).equals(value)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) {
         if (info.getType() != TvInputInfo.TYPE_HDMI) {
             throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
         }
         synchronized (mLock) {
             String parentId = info.getParentId();
-            int parentIndex = mHardwareInputIdMap.indexOfValue(parentId);
-            if (parentIndex < 0 || !parentId.equals(mHardwareInputIdMap.valueAt(parentIndex))) {
+            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
+            if (parentIndex < 0) {
                 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
             }
             String oldInputId = mHdmiCecInputIdMap.get(logicalAddress);
@@ -259,11 +265,11 @@
     public void removeTvInput(String inputId) {
         synchronized (mLock) {
             mInputMap.remove(inputId);
-            int hardwareIndex = mHardwareInputIdMap.indexOfValue(inputId);
+            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
             if (hardwareIndex >= 0) {
                 mHardwareInputIdMap.removeAt(hardwareIndex);
             }
-            int cecIndex = mHdmiCecInputIdMap.indexOfValue(inputId);
+            int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId);
             if (cecIndex >= 0) {
                 mHdmiCecInputIdMap.removeAt(cecIndex);
             }
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 87626d0..46af471 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -861,6 +861,7 @@
 
 static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
     const char* doubleSignature = "(D)V";
+    const char* longSignature = "(J)V";
 
     jclass gpsClockClass = env->FindClass("android/location/GpsClock");
     jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
@@ -873,53 +874,51 @@
         env->CallObjectMethod(gpsClockObject, setterMethod, clock->leap_second);
    }
 
-    jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", "(J)V");
+   jmethodID typeSetterMethod = env->GetMethodID(gpsClockClass, "setType", "(B)V");
+   env->CallObjectMethod(gpsClockObject, typeSetterMethod, clock->type);
+
+    jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", longSignature);
     env->CallObjectMethod(gpsClockObject, setterMethod, clock->time_ns);
 
     if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsClockClass,
-                "setTimeUncertaintyInNs",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsClockClass, "setTimeUncertaintyInNs", doubleSignature);
         env->CallObjectMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns);
     }
 
+    if (flags & GPS_CLOCK_HAS_FULL_BIAS) {
+        jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setFullBiasInNs", longSignature);
+        env->CallObjectMethod(gpsClockObject, setterMethod, clock->full_bias_ns);
+    }
+
     if (flags & GPS_CLOCK_HAS_BIAS) {
         jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature);
         env->CallObjectMethod(gpsClockObject, setterMethod, clock->bias_ns);
     }
 
     if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsClockClass,
-                "setBiasUncertaintyInNs",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsClockClass, "setBiasUncertaintyInNs", doubleSignature);
         env->CallObjectMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns);
     }
 
     if (flags & GPS_CLOCK_HAS_DRIFT) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsClockClass,
-                "setDriftInNsPerSec",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsClockClass, "setDriftInNsPerSec", doubleSignature);
         env->CallObjectMethod(gpsClockObject, setterMethod, clock->drift_nsps);
     }
 
     if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsClockClass,
-                "setDriftUncertaintyInNsPerSec",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsClockClass, "setDriftUncertaintyInNsPerSec", doubleSignature);
         env->CallObjectMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
     }
 
     return gpsClockObject;
 }
 
-static jobject translate_gps_measurement(
-        JNIEnv* env,
-        GpsMeasurement* measurement,
-        uint32_t time_ns) {
+static jobject translate_gps_measurement(JNIEnv* env, GpsMeasurement* measurement) {
+    const char* byteSignature = "(B)V";
     const char* shortSignature = "(S)V";
     const char* longSignature = "(J)V";
     const char* floatSignature = "(F)V";
@@ -931,16 +930,18 @@
     jobject gpsMeasurementObject = env->NewObject(gpsMeasurementClass, gpsMeasurementCtor);
     GpsMeasurementFlags flags = measurement->flags;
 
-
-    jmethodID prnSetterMethod = env->GetMethodID(gpsMeasurementClass, "setPrn", "(B)V");
+    jmethodID prnSetterMethod = env->GetMethodID(gpsMeasurementClass, "setPrn", byteSignature);
     env->CallObjectMethod(gpsMeasurementObject, prnSetterMethod, measurement->prn);
 
-    jmethodID localTimeSetterMethod =
-            env->GetMethodID(gpsMeasurementClass, "setLocalTimeInNs", longSignature);
+    jmethodID timeOffsetSetterMethod =
+            env->GetMethodID(gpsMeasurementClass, "setTimeOffsetInNs", doubleSignature);
     env->CallObjectMethod(
             gpsMeasurementObject,
-            localTimeSetterMethod,
-            time_ns + measurement->time_offset_ns);
+            timeOffsetSetterMethod,
+            measurement->time_offset_ns);
+
+    jmethodID stateSetterMethod = env->GetMethodID(gpsMeasurementClass, "setState", shortSignature);
+    env->CallObjectMethod(gpsMeasurementObject, stateSetterMethod, measurement->state);
 
     jmethodID receivedGpsTowSetterMethod =
             env->GetMethodID(gpsMeasurementClass, "setReceivedGpsTowInNs", longSignature);
@@ -949,10 +950,8 @@
             receivedGpsTowSetterMethod,
             measurement->received_gps_tow_ns);
 
-    jmethodID cn0SetterMethod = env->GetMethodID(
-            gpsMeasurementClass,
-            "setCn0InDbHz",
-            doubleSignature);
+    jmethodID cn0SetterMethod =
+            env->GetMethodID(gpsMeasurementClass, "setCn0InDbHz", doubleSignature);
     env->CallObjectMethod(gpsMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz);
 
     jmethodID pseudorangeRateSetterMethod = env->GetMethodID(
@@ -962,7 +961,7 @@
     env->CallObjectMethod(
             gpsMeasurementObject,
             pseudorangeRateSetterMethod,
-            measurement->pseudorange_rate_mpersec);
+            measurement->pseudorange_rate_mps);
 
     jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID(
             gpsMeasurementClass,
@@ -971,7 +970,14 @@
     env->CallObjectMethod(
             gpsMeasurementObject,
             pseudorangeRateUncertaintySetterMethod,
-            measurement->pseudorange_rate_uncertainty_mpersec);
+            measurement->pseudorange_rate_uncertainty_mps);
+
+    jmethodID accumulatedDeltaRangeStateSetterMethod =
+            env->GetMethodID(gpsMeasurementClass, "setAccumulatedDeltaRangeState", shortSignature);
+    env->CallObjectMethod(
+            gpsMeasurementObject,
+            accumulatedDeltaRangeStateSetterMethod,
+            measurement->accumulated_delta_range_state);
 
     jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID(
             gpsMeasurementClass,
@@ -991,12 +997,9 @@
             accumulatedDeltaRangeUncertaintySetterMethod,
             measurement->accumulated_delta_range_uncertainty_m);
 
-
     if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setPseudorangeInMeters",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setPseudorangeInMeters", doubleSignature);
         env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->pseudorange_m);
     }
 
@@ -1012,10 +1015,8 @@
     }
 
     if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setCodePhaseInChips",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setCodePhaseInChips", doubleSignature);
         env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->code_phase_chips);
     }
 
@@ -1031,10 +1032,8 @@
     }
 
     if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setCarrierFrequencyInHz",
-                 floatSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setCarrierFrequencyInHz", floatSignature);
         env->CallObjectMethod(
                 gpsMeasurementObject,
                 setterMethod,
@@ -1065,33 +1064,27 @@
     }
 
     jmethodID lossOfLockSetterMethod =
-            env->GetMethodID(gpsMeasurementClass, "setLossOfLock", shortSignature);
+            env->GetMethodID(gpsMeasurementClass, "setLossOfLock", byteSignature);
     env->CallObjectMethod(gpsMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock);
 
     if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setBitNumber",
-                shortSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setBitNumber", shortSignature);
         env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->bit_number);
     }
 
     if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setTimeFromLastBitInNs",
-                longSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setTimeFromLastBitInMs", shortSignature);
         env->CallObjectMethod(
                 gpsMeasurementObject,
                 setterMethod,
-                measurement->time_from_last_bit_ns);
+                measurement->time_from_last_bit_ms);
     }
 
     if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setDopplerShiftInHz",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setDopplerShiftInHz", doubleSignature);
         env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->doppler_shift_hz);
     }
 
@@ -1106,10 +1099,8 @@
                 measurement->doppler_shift_uncertainty_hz);
     }
 
-    jmethodID multipathIndicatorSetterMethod = env->GetMethodID(
-            gpsMeasurementClass,
-            "setMultipathIndicator",
-            shortSignature);
+    jmethodID multipathIndicatorSetterMethod =
+            env->GetMethodID(gpsMeasurementClass, "setMultipathIndicator", byteSignature);
     env->CallObjectMethod(
             gpsMeasurementObject,
             multipathIndicatorSetterMethod,
@@ -1122,18 +1113,14 @@
     }
 
     if (flags & GPS_MEASUREMENT_HAS_ELEVATION) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setElevationInDeg",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setElevationInDeg", doubleSignature);
         env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->elevation_deg);
     }
 
     if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) {
-        jmethodID setterMethod = env->GetMethodID(
-                gpsMeasurementClass,
-                "setElevationUncertaintyInDeg",
-                doubleSignature);
+        jmethodID setterMethod =
+                env->GetMethodID(gpsMeasurementClass, "setElevationUncertaintyInDeg", doubleSignature);
         env->CallObjectMethod(
                 gpsMeasurementObject,
                 setterMethod,
@@ -1180,10 +1167,7 @@
 
     GpsMeasurement* gpsMeasurements = data->measurements;
     for (uint16_t i = 0; i < measurementCount; ++i) {
-        jobject gpsMeasurement = translate_gps_measurement(
-                env,
-                &gpsMeasurements[i],
-                data->clock.time_ns);
+        jobject gpsMeasurement = translate_gps_measurement(env, &gpsMeasurements[i]);
         env->SetObjectArrayElement(gpsMeasurementArray, i, gpsMeasurement);
         env->DeleteLocalRef(gpsMeasurement);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 12ed920..2a11252 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3708,8 +3708,8 @@
     }
 
     @Override
-    public boolean setApplicationBlocked(ComponentName who, String packageName,
-            boolean blocked) {
+    public boolean setApplicationHidden(ComponentName who, String packageName,
+            boolean hidden) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
@@ -3720,10 +3720,10 @@
             long id = Binder.clearCallingIdentity();
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
-                return pm.setApplicationBlockedSettingAsUser(packageName, blocked, callingUserId);
+                return pm.setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
             } catch (RemoteException re) {
                 // shouldn't happen
-                Slog.e(LOG_TAG, "Failed to setApplicationBlockedSetting", re);
+                Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
             } finally {
                 restoreCallingIdentity(id);
             }
@@ -3732,7 +3732,7 @@
     }
 
     @Override
-    public int setApplicationsBlocked(ComponentName who, Intent intent, boolean blocked) {
+    public int setApplicationsHidden(ComponentName who, Intent intent, boolean hidden) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
@@ -3750,20 +3750,20 @@
                         callingUserId);
 
                 if (DBG) Slog.d(LOG_TAG, "Enabling activities: " + activitiesToEnable);
-                int numberOfAppsUnblocked = 0;
+                int numberOfAppsUnhidden = 0;
                 if (activitiesToEnable != null) {
                     for (ResolveInfo info : activitiesToEnable) {
                         if (info.activityInfo != null) {
-                            numberOfAppsUnblocked++;
-                            pm.setApplicationBlockedSettingAsUser(info.activityInfo.packageName,
-                                    blocked, callingUserId);
+                            numberOfAppsUnhidden++;
+                            pm.setApplicationHiddenSettingAsUser(info.activityInfo.packageName,
+                                    hidden, callingUserId);
                         }
                     }
                 }
-                return numberOfAppsUnblocked;
+                return numberOfAppsUnhidden;
             } catch (RemoteException re) {
                 // shouldn't happen
-                Slog.e(LOG_TAG, "Failed to setApplicationsBlockedSettingsWithIntent", re);
+                Slog.e(LOG_TAG, "Failed to setApplicationsHiddenSettingsWithIntent", re);
             } finally {
                 restoreCallingIdentity(id);
             }
@@ -3772,7 +3772,7 @@
     }
 
     @Override
-    public boolean isApplicationBlocked(ComponentName who, String packageName) {
+    public boolean isApplicationHidden(ComponentName who, String packageName) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
             if (who == null) {
@@ -3783,10 +3783,10 @@
             long id = Binder.clearCallingIdentity();
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
-                return pm.getApplicationBlockedSettingAsUser(packageName, callingUserId);
+                return pm.getApplicationHiddenSettingAsUser(packageName, callingUserId);
             } catch (RemoteException re) {
                 // shouldn't happen
-                Slog.e(LOG_TAG, "Failed to getApplicationBlockedSettingAsUser", re);
+                Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
             } finally {
                 restoreCallingIdentity(id);
             }
@@ -4030,7 +4030,8 @@
 
     @Override
     public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
-        enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+        // TODO: Should there be a check to make sure this relationship is within a profile group?
+        //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
         synchronized (this) {
             ActiveAdmin admin = getProfileOwnerAdmin(userId);
             return (admin != null) ? admin.disableCallerId : false;
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
index 4fe30e6..fb29b6a 100644
--- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -40,6 +40,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IUserManager;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -103,7 +104,8 @@
 
         @Override
         public void requestPermission(final String packageName, final String requestType,
-                final Bundle requestData) throws RemoteException {
+                final String requestId,
+                final PersistableBundle requestData) throws RemoteException {
             if (DEBUG) {
                 Log.i(LOG_TAG, "requestPermission");
             }
@@ -127,6 +129,7 @@
                     intent.setComponent(restrictionsProvider);
                     intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
                     intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType);
+                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_ID, requestId);
                     intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
                     mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
                 } finally {
@@ -136,7 +139,40 @@
         }
 
         @Override
-        public void notifyPermissionResponse(String packageName, Bundle response)
+        public Intent getLocalApprovalIntent() throws RemoteException {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "requestPermission");
+            }
+            final int userHandle = UserHandle.getCallingUserId();
+            if (mDpm != null) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    ComponentName restrictionsProvider =
+                            mDpm.getRestrictionsProvider(userHandle);
+                    // Check if there is a restrictions provider
+                    if (restrictionsProvider == null) {
+                        throw new IllegalStateException(
+                            "Cannot request permission without a restrictions provider registered");
+                    }
+                    String providerPackageName = restrictionsProvider.getPackageName();
+                    Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_LOCAL_APPROVAL);
+                    intent.setPackage(providerPackageName);
+                    ResolveInfo ri = AppGlobals.getPackageManager().resolveIntent(intent,
+                            null /* resolvedType */, 0 /* flags */, userHandle);
+                    if (ri != null && ri.activityInfo != null && ri.activityInfo.exported) {
+                        intent.setComponent(new ComponentName(ri.activityInfo.packageName,
+                                ri.activityInfo.name));
+                        return intent;
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void notifyPermissionResponse(String packageName, PersistableBundle response)
                 throws RemoteException {
             // Check caller
             int callingUid = Binder.getCallingUid();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 13cbf8a..475824af 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.PackageUsageStats;
@@ -25,6 +26,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -381,10 +383,21 @@
 
     private class BinderService extends IUsageStatsManager.Stub {
 
+        private boolean hasPermission(String callingPackage) {
+            final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+                    Binder.getCallingUid(), callingPackage);
+            if (mode == AppOpsManager.MODE_IGNORED) {
+                // If AppOpsManager ignores this, still allow if we have the system level
+                // permission.
+                return getContext().checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+                        == PackageManager.PERMISSION_GRANTED;
+            }
+            return mode == AppOpsManager.MODE_ALLOWED;
+        }
+
         @Override
         public UsageStats[] getStatsSince(int bucketType, long time, String callingPackage) {
-            if (mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            if (!hasPermission(callingPackage)) {
                 return UsageStats.EMPTY_STATS;
             }
 
@@ -398,8 +411,7 @@
 
         @Override
         public UsageStats.Event[] getEventsSince(long time, String callingPackage) {
-            if (mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            if (!hasPermission(callingPackage)) {
                 return UsageStats.Event.EMPTY_EVENTS;
             }
 
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index f751826..d5b39cf 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -432,14 +432,20 @@
             public void onSuccess(ConnectionRequest request, Connection connection) {
                 Log.d(this, "adapter handleCreateConnectionSuccessful %s",
                         request.getCallId());
-                mAdapter.handleCreateConnectionSuccessful(request);
                 addConnection(request.getCallId(), connection);
-
-                // TODO: onSuccess should pass through the entire state of the connection instead of
-                // having to set it like this afterwards.  Also, it would eliminate the hack of
-                // having to change the request object that we pass back.
-                mConnectionListener.onCallCapabilitiesChanged(
-                        connection, connection.getCallCapabilities());
+                mAdapter.handleCreateConnectionSuccessful(
+                        request,
+                        new ParcelableConnection(
+                                request.getAccountHandle(),
+                                connection.getState(),
+                                connection.getCallCapabilities(),
+                                connection.getHandle(),
+                                connection.getHandlePresentation(),
+                                connection.getCallerDisplayName(),
+                                connection.getCallerDisplayNamePresentation(),
+                                connection.getCallVideoProvider() == null ?
+                                        null : connection.getCallVideoProvider().getInterface(),
+                                connection.getVideoState()));
             }
 
             @Override
@@ -720,12 +726,6 @@
         mIdByConnection.put(connection, callId);
         connection.addConnectionListener(mConnectionListener);
         onConnectionAdded(connection);
-
-        // Trigger listeners for properties set before connection listener was added.
-        CallVideoProvider callVideoProvider = connection.getCallVideoProvider();
-        if (callVideoProvider != null) {
-            connection.setCallVideoProvider(callVideoProvider);
-        }
     }
 
     private void removeConnection(Connection connection) {
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index 0b8d93d..66e9925 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -74,10 +74,11 @@
         }
     }
 
-    void handleCreateConnectionSuccessful(ConnectionRequest request) {
+    void handleCreateConnectionSuccessful(
+            ConnectionRequest request, ParcelableConnection connection) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.handleCreateConnectionSuccessful(request);
+                adapter.handleCreateConnectionSuccessful(request, connection);
             } catch (RemoteException e) {
             }
         }
@@ -277,7 +278,9 @@
     void setCallVideoProvider(String callId, CallVideoProvider callVideoProvider) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setCallVideoProvider(callId, callVideoProvider.getInterface());
+                adapter.setCallVideoProvider(
+                        callId,
+                        callVideoProvider == null ? null : callVideoProvider.getInterface());
             } catch (RemoteException e) {
             }
         }
diff --git a/telecomm/java/android/telecomm/ParcelableConnection.aidl b/telecomm/java/android/telecomm/ParcelableConnection.aidl
new file mode 100644
index 0000000..e3c3bd2
--- /dev/null
+++ b/telecomm/java/android/telecomm/ParcelableConnection.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+parcelable ParcelableConnection;
diff --git a/telecomm/java/android/telecomm/ParcelableConnection.java b/telecomm/java/android/telecomm/ParcelableConnection.java
new file mode 100644
index 0000000..f730fef
--- /dev/null
+++ b/telecomm/java/android/telecomm/ParcelableConnection.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telecomm.ICallVideoProvider;
+
+/**
+ * Information about a connection that is used between Telecomm and the ConnectionService.
+ * This is used to send initial Connection information to Telecomm when the connection is
+ * first created.
+ * @hide
+ */
+public final class ParcelableConnection implements Parcelable {
+    private PhoneAccountHandle mPhoneAccount;
+    private int mState;
+    private int mCapabilities;
+    private Uri mHandle;
+    private int mHandlePresentation;
+    private String mCallerDisplayName;
+    private int mCallerDisplayNamePresentation;
+    private ICallVideoProvider mCallVideoProvider;
+    private int mVideoState;
+
+    /** @hide */
+    public ParcelableConnection(
+            PhoneAccountHandle phoneAccount,
+            int state,
+            int capabilities,
+            Uri handle,
+            int handlePresentation,
+            String callerDisplayName,
+            int callerDisplayNamePresentation,
+            ICallVideoProvider callVideoProvider,
+            int videoState) {
+        mPhoneAccount = phoneAccount;
+        mState = state;
+        mCapabilities = capabilities;
+        mHandle = handle;
+        mHandlePresentation = handlePresentation;
+        mCallerDisplayName = callerDisplayName;
+        mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+        mCallVideoProvider = callVideoProvider;
+        mVideoState = videoState;
+    }
+
+    public PhoneAccountHandle getPhoneAccount() {
+        return mPhoneAccount;
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+    public int getCapabilities() {
+        return mCapabilities;
+    }
+
+    public Uri getHandle() {
+        return mHandle;
+    }
+
+    public int getHandlePresentation() {
+        return mHandlePresentation;
+    }
+
+    public String getCallerDisplayName() {
+        return mCallerDisplayName;
+    }
+
+    public int getCallerDisplayNamePresentation() {
+        return mCallerDisplayNamePresentation;
+    }
+
+    public ICallVideoProvider getCallVideoProvider() {
+        return mCallVideoProvider;
+    }
+
+    public int getVideoState() {
+        return mVideoState;
+    }
+
+    public static final Parcelable.Creator<ParcelableConnection> CREATOR =
+            new Parcelable.Creator<ParcelableConnection> () {
+        @Override
+        public ParcelableConnection createFromParcel(Parcel source) {
+            ClassLoader classLoader = ParcelableConnection.class.getClassLoader();
+
+            PhoneAccountHandle phoneAccount = source.readParcelable(classLoader);
+            int state = source.readInt();
+            int capabilities = source.readInt();
+            Uri handle = source.readParcelable(classLoader);
+            int handlePresentation = source.readInt();
+            String callerDisplayName = source.readString();
+            int callerDisplayNamePresentation = source.readInt();
+            ICallVideoProvider callVideoProvider =
+                    ICallVideoProvider.Stub.asInterface(source.readStrongBinder());
+            int videoState = source.readInt();
+
+            return new ParcelableConnection(
+                    phoneAccount,
+                    state,
+                    capabilities,
+                    handle,
+                    handlePresentation,
+                    callerDisplayName,
+                    callerDisplayNamePresentation,
+                    callVideoProvider,
+                    videoState);
+        }
+
+        @Override
+        public ParcelableConnection[] newArray(int size) {
+            return new ParcelableConnection[size];
+        }
+    };
+
+    /** {@inheritDoc} */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Writes ParcelableConnection object into a Parcel. */
+    @Override
+    public void writeToParcel(Parcel destination, int flags) {
+        destination.writeParcelable(mPhoneAccount, 0);
+        destination.writeInt(mState);
+        destination.writeInt(mCapabilities);
+        destination.writeParcelable(mHandle, 0);
+        destination.writeInt(mHandlePresentation);
+        destination.writeString(mCallerDisplayName);
+        destination.writeInt(mCallerDisplayNamePresentation);
+        destination.writeStrongBinder(
+                mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null);
+        destination.writeInt(mVideoState);
+    }
+}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 0570d18..5a7753c 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources.NotFoundException;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Parcel;
@@ -181,7 +182,7 @@
         }
         try {
             return packageContext.getResources().getDrawable(resId);
-        } catch (MissingResourceException e) {
+        } catch (NotFoundException|MissingResourceException e) {
             Log.e(this, e, "Cannot find icon %d in package %s",
                     resId, mAccountHandle.getComponentName().getPackageName());
             return null;
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
index 170cf04..b8b8b9d 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
@@ -59,8 +59,11 @@
      *
      * @param cameraId The id of the camera.
      */
-    public void setCamera(String cameraId) throws RemoteException {
-        mCallVideoProvider.setCamera(cameraId);
+    public void setCamera(String cameraId) {
+        try {
+            mCallVideoProvider.setCamera(cameraId);
+        } catch (RemoteException e) {
+        }
     }
 
     /**
@@ -107,8 +110,11 @@
      *
      * @param value The camera zoom ratio.
      */
-    public void setZoom(float value) throws RemoteException {
-        mCallVideoProvider.setZoom(value);
+    public void setZoom(float value) {
+        try {
+            mCallVideoProvider.setZoom(value);
+        } catch (RemoteException e) {
+        }
     }
 
     /**
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 29461af..10569ab 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -19,18 +19,17 @@
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.net.Uri;
-import android.os.IBinder.DeathRecipient;
 import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.DisconnectCause;
-
 import android.text.TextUtils;
 
 import com.android.internal.os.SomeArgs;
+import com.android.internal.telecomm.ICallVideoProvider;
 import com.android.internal.telecomm.IConnectionService;
 import com.android.internal.telecomm.IConnectionServiceAdapter;
-import com.android.internal.telecomm.ICallVideoProvider;
 import com.android.internal.telecomm.RemoteServiceCallback;
 
 import java.util.LinkedList;
@@ -80,11 +79,27 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: {
-                    ConnectionRequest request = (ConnectionRequest) msg.obj;
-                    if (isPendingConnection(request.getCallId())) {
-                        mConnection = new RemoteConnection(mConnectionService, request.getCallId());
-                        mPendingResponse.onSuccess(request, mConnection);
-                        clearPendingInformation();
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        ConnectionRequest request = (ConnectionRequest) args.arg1;
+                        if (isPendingConnection(request.getCallId())) {
+                            ParcelableConnection parcel = (ParcelableConnection) args.arg2;
+                            mConnection = new RemoteConnection(
+                                    mConnectionService, request.getCallId());
+                            mConnection.setState(parcel.getState());
+                            mConnection.setCallCapabilities(parcel.getCapabilities());
+                            mConnection.setHandle(
+                                    parcel.getHandle(), parcel.getHandlePresentation());
+                            mConnection.setCallerDisplayName(
+                                    parcel.getCallerDisplayName(),
+                                    parcel.getCallerDisplayNamePresentation());
+                            // TODO: Do we need to support video providers for remote connections?
+
+                            mPendingResponse.onSuccess(request, mConnection);
+                            clearPendingInformation();
+                        }
+                    } finally {
+                        args.recycle();
                     }
                     break;
                 }
@@ -242,8 +257,12 @@
 
     private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
         @Override
-        public void handleCreateConnectionSuccessful(ConnectionRequest request) {
-            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, request).sendToTarget();
+        public void handleCreateConnectionSuccessful(
+                ConnectionRequest request, ParcelableConnection connection) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = request;
+            args.arg2 = connection;
+            mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget();
         }
 
         @Override
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index c76d670..ea89a53 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -56,6 +56,12 @@
             "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
 
     /**
+     * The {@link android.content.Intent} action used to show the call settings page.
+     */
+    public static final String ACTION_SHOW_CALL_SETTINGS =
+            "android.telecomm.intent.action.SHOW_CALL_SETTINGS";
+
+    /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
      * determines whether the speakerphone should be automatically turned on for an outgoing call.
      */
diff --git a/telecomm/java/android/telecomm/VideoCallProfile.java b/telecomm/java/android/telecomm/VideoCallProfile.java
index fc7b2c3..5b15e4a 100644
--- a/telecomm/java/android/telecomm/VideoCallProfile.java
+++ b/telecomm/java/android/telecomm/VideoCallProfile.java
@@ -77,6 +77,15 @@
      * Creates an instance of the VideoCallProfile
      *
      * @param videoState The video state.
+     */
+    public VideoCallProfile(int videoState) {
+        this(videoState, QUALITY_DEFAULT);
+    }
+
+    /**
+     * Creates an instance of the VideoCallProfile
+     *
+     * @param videoState The video state.
      * @param quality The video quality.
      */
     public VideoCallProfile(int videoState, int quality) {
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index 1abdf7c..552993f 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -19,6 +19,7 @@
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.telecomm.ConnectionRequest;
+import android.telecomm.ParcelableConnection;
 import android.telecomm.StatusHints;
 
 import com.android.internal.telecomm.ICallVideoProvider;
@@ -32,7 +33,8 @@
  * {@hide}
  */
 oneway interface IConnectionServiceAdapter {
-    void handleCreateConnectionSuccessful(in ConnectionRequest request);
+    void handleCreateConnectionSuccessful(
+            in ConnectionRequest request, in ParcelableConnection connection);
 
     void handleCreateConnectionFailed(
             in ConnectionRequest request, int errorCode, String errorMessage);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index e388480..36c90f2 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -661,7 +661,7 @@
      * @hide
      */
     @Override
-    public boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked,
+    public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
             UserHandle user) {
         return false;
     }
@@ -670,7 +670,7 @@
      * @hide
      */
     @Override
-    public boolean getApplicationBlockedSettingAsUser(String packageName, UserHandle user) {
+    public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
         return false;
     }
 
diff --git a/tests/DozeTest/AndroidManifest.xml b/tests/DozeTest/AndroidManifest.xml
index c199f69..03778d6 100644
--- a/tests/DozeTest/AndroidManifest.xml
+++ b/tests/DozeTest/AndroidManifest.xml
@@ -22,7 +22,8 @@
             android:name="DozeTestDream"
             android:exported="true"
             android:icon="@drawable/ic_app"
-            android:label="@string/doze_dream_name">
+            android:label="@string/doze_dream_name"
+            android:permission="android.permission.BIND_DREAM_SERVICE">
             <!-- Commented out to prevent this dream from appearing in the list of
                  dreams that the user can select via the Settings application.
             <intent-filter>
diff --git a/tests/DozeTest/res/layout/dream.xml b/tests/DozeTest/res/layout/dream.xml
index 1c8fd3f..bced230 100644
--- a/tests/DozeTest/res/layout/dream.xml
+++ b/tests/DozeTest/res/layout/dream.xml
@@ -18,7 +18,8 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:gravity="center_vertical"
-        android:orientation="vertical">
+        android:orientation="vertical"
+        android:background="#bb2288">
     <TextView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index a0b2d1a..f72e331 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -22,11 +22,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.service.dreams.DozeHardware;
 import android.service.dreams.DreamService;
 import android.text.format.DateFormat;
 import android.util.Log;
+import android.view.Display;
 import android.widget.TextView;
 
 import java.util.Date;
@@ -51,10 +53,16 @@
     // Doesn't mean anything.  Real hardware won't handle it.
     private static final String TEST_PING_MESSAGE = "test.ping";
 
+    // Not all hardware supports dozing.  We should use Display.STATE_DOZE but
+    // for testing purposes it is convenient to use Display.STATE_ON so the
+    // test still works on hardware that does not support dozing.
+    private static final int DISPLAY_STATE_WHEN_DOZING = Display.STATE_ON;
+
     private PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
     private AlarmManager mAlarmManager;
     private PendingIntent mAlarmIntent;
+    private Handler mHandler = new Handler();
 
     private TextView mAlarmClock;
 
@@ -64,6 +72,8 @@
     private boolean mDreaming;
     private DozeHardware mDozeHardware;
 
+    private long mLastTime = Long.MIN_VALUE;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -80,6 +90,8 @@
         registerReceiver(mAlarmReceiver, filter);
         mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
                 PendingIntent.FLAG_CANCEL_CURRENT);
+
+        setDozeScreenState(DISPLAY_STATE_WHEN_DOZING);
     }
 
     @Override
@@ -143,13 +155,33 @@
         if (mDreaming) {
             long now = System.currentTimeMillis();
             now -= now % 60000; // back up to last minute boundary
+            if (mLastTime == now) {
+                return;
+            }
 
+            mLastTime = now;
             mTime.setTime(now);
             mAlarmClock.setText(mTimeFormat.format(mTime));
 
             mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, now + 60000, mAlarmIntent);
 
-            mWakeLock.acquire(UPDATE_TIME_TIMEOUT);
+            mWakeLock.acquire(UPDATE_TIME_TIMEOUT + 5000 /*for testing brightness*/);
+
+            // flash the screen a bit to test these functions
+            setDozeScreenState(DISPLAY_STATE_WHEN_DOZING);
+            setDozeScreenBrightness(200);
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    setDozeScreenBrightness(50);
+                }
+            }, 2000);
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    setDozeScreenState(Display.STATE_OFF);
+                }
+            }, 5000);
         }
     }
 
diff --git a/tests/MusicBrowserDemo/Android.mk b/tests/MusicBrowserDemo/Android.mk
new file mode 100644
index 0000000..207774b
--- /dev/null
+++ b/tests/MusicBrowserDemo/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := MusicBrowserDemo
+#LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-appcompat
+
+LOCAL_RESOURCE_DIR := \
+        $(LOCAL_PATH)/res \
+        frameworks/support/v7/appcompat/res
+LOCAL_PROGUARD_ENABLED := disabled
+#LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_AAPT_FLAGS := \
+        --auto-add-overlay \
+        --extra-packages android.support.v7.appcompat
+include $(BUILD_PACKAGE)
diff --git a/tests/MusicBrowserDemo/AndroidManifest.xml b/tests/MusicBrowserDemo/AndroidManifest.xml
new file mode 100644
index 0000000..d2acfe2
--- /dev/null
+++ b/tests/MusicBrowserDemo/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.musicbrowserdemo"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="9"
+        android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme"
+        >
+
+        <activity
+            android:name="com.example.android.musicbrowserdemo.MainActivity"
+            android:label="@string/app_name"
+            >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
diff --git a/tests/MusicBrowserDemo/res/drawable-hdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..47d6854
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/drawable-mdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..01b53fd
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/drawable-xhdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..af762f2
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/drawable-xxhdpi/ic_launcher.png b/tests/MusicBrowserDemo/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..eef47aa
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicBrowserDemo/res/values/strings.xml b/tests/MusicBrowserDemo/res/values/strings.xml
new file mode 100644
index 0000000..858f278f
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+<resources>
+
+    <string name="app_name">Music Browser</string>
+
+</resources>
diff --git a/tests/MusicBrowserDemo/res/values/styles.xml b/tests/MusicBrowserDemo/res/values/styles.xml
new file mode 100644
index 0000000..b83662d
--- /dev/null
+++ b/tests/MusicBrowserDemo/res/values/styles.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
new file mode 100644
index 0000000..c0f3a7f
--- /dev/null
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.example.android.musicbrowserdemo;
+
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.browse.MediaBrowserService;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.ListFragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO: Include an icon.
+
+public class AppListFragment extends ListFragment {
+
+    private Adapter mAdapter;
+    private List<Item> mItems;
+
+    public AppListFragment() {
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mAdapter = new Adapter();
+        setListAdapter(mAdapter);
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        final Item item = mItems.get(position);
+
+        Log.i("AppListFragment", "Item clicked: " + position + " -- " + item.component);
+
+        final BrowserListFragment fragment = new BrowserListFragment();
+
+        final Bundle args = new Bundle();
+        args.putParcelable(BrowserListFragment.ARG_COMPONENT, item.component);
+        fragment.setArguments(args);
+
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, fragment)
+                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+                .addToBackStack(null)
+                .commit();
+    }
+
+    private static class Item {
+        final String label;
+        final ComponentName component;
+
+        Item(String l, ComponentName c) {
+            this.label = l;
+            this.component = c;
+        }
+    }
+
+    private class Adapter extends BaseAdapter {
+        private final LayoutInflater mInflater;
+
+        Adapter() {
+            super();
+
+            final Context context = getActivity();
+            mInflater = LayoutInflater.from(context);
+
+            // Load the data
+            final PackageManager pm = context.getPackageManager();
+            final Intent intent = new Intent(MediaBrowserService.SERVICE_ACTION);
+            final List<ResolveInfo> list = pm.queryIntentServices(intent, 0);
+            final int N = list.size();
+            mItems = new ArrayList(N);
+            for (int i=0; i<N; i++) {
+                final ResolveInfo ri = list.get(i);
+                mItems.add(new Item(ri.loadLabel(pm).toString(), new ComponentName(
+                            ri.serviceInfo.applicationInfo.packageName,
+                            ri.serviceInfo.name)));
+            }
+        }
+
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public Item getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return 1;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
+            }
+
+            final TextView tv = (TextView)convertView;
+            final Item item = mItems.get(position);
+            tv.setText(item.label);
+
+            return convertView;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
+    }
+}
+
+
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
new file mode 100644
index 0000000..3fc468d
--- /dev/null
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2010 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.example.android.musicbrowserdemo;
+
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowserItem;
+import android.media.browse.MediaBrowserService;
+import android.os.Bundle;
+import android.net.Uri;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.ListFragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BrowserListFragment extends ListFragment {
+    private static final String TAG = "BrowserListFragment";
+
+    // Hints
+    public static final String HINT_DISPLAY = "com.example.android.musicbrowserdemo.DISPLAY";
+
+    // For args
+    public static final String ARG_COMPONENT = "component";
+    public static final String ARG_URI = "uri";
+
+    private Adapter mAdapter;
+    private List<Item> mItems = new ArrayList();
+    private ComponentName mComponent;
+    private Uri mUri;
+    private MediaBrowser mBrowser;
+
+    private static class Item {
+        final MediaBrowserItem media;
+
+        Item(MediaBrowserItem m) {
+            this.media = m;
+        }
+    }
+
+    public BrowserListFragment() {
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        Log.d(TAG, "onActivityCreated -- " + hashCode());
+        mAdapter = new Adapter();
+        setListAdapter(mAdapter);
+
+        // Get our arguments
+        final Bundle args = getArguments();
+        mComponent = args.getParcelable(ARG_COMPONENT);
+        mUri = args.getParcelable(ARG_URI);
+
+        // A hint about who we are, so the service can customize the results if it wants to.
+        final Bundle rootHints = new Bundle();
+        rootHints.putBoolean(HINT_DISPLAY, true);
+
+        mBrowser = new MediaBrowser(getActivity(), mComponent, mConnectionCallbacks, rootHints);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mBrowser.connect();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mBrowser.disconnect();
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        final Item item = mItems.get(position);
+
+        Log.i("BrowserListFragment", "Item clicked: " + position + " -- "
+                + mAdapter.getItem(position).media.getUri());
+
+        final BrowserListFragment fragment = new BrowserListFragment();
+
+        final Bundle args = new Bundle();
+        args.putParcelable(BrowserListFragment.ARG_COMPONENT, mComponent);
+        args.putParcelable(BrowserListFragment.ARG_URI, item.media.getUri());
+        fragment.setArguments(args);
+
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, fragment)
+                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+                .addToBackStack(null)
+                .commit();
+
+    }
+
+    final MediaBrowser.ConnectionCallback mConnectionCallbacks
+            = new MediaBrowser.ConnectionCallback() {
+        @Override
+        public void onConnected() {
+            Log.d(TAG, "mConnectionCallbacks.onConnected");
+            if (mUri == null) {
+                mUri = mBrowser.getRoot();
+            }
+            mBrowser.subscribe(mUri, new MediaBrowser.SubscriptionCallback() {
+                    @Override
+                    public void onChildrenLoaded(Uri parentUri, List<MediaBrowserItem> children) {
+                        Log.d(TAG, "onChildrenLoaded parentUri=" + parentUri
+                                + " children= " + children);
+                        mItems.clear();
+                        final int N = children.size();
+                        for (int i=0; i<N; i++) {
+                            mItems.add(new Item(children.get(i)));
+                        }
+                        mAdapter.notifyDataSetChanged();
+                    }
+
+                    @Override
+                    public void onError(Uri parentUri) {
+                        Log.d(TAG, "onError parentUri=" + parentUri);
+                    }
+                });
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            Log.d(TAG, "mConnectionCallbacks.onConnectionSuspended");
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            Log.d(TAG, "mConnectionCallbacks.onConnectionFailed");
+        }
+    };
+
+    private class Adapter extends BaseAdapter {
+        private final LayoutInflater mInflater;
+
+        Adapter() {
+            super();
+
+            final Context context = getActivity();
+            mInflater = LayoutInflater.from(context);
+        }
+
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public Item getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return 1;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
+            }
+
+            final TextView tv = (TextView)convertView;
+            final Item item = mItems.get(position);
+            tv.setText(item.media.getTitle());
+
+            return convertView;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
+    }
+}
+
+
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
new file mode 100644
index 0000000..ed91aad
--- /dev/null
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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.example.android.musicbrowserdemo;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * Main activity class.
+ */
+public class MainActivity extends FragmentActivity {
+
+    private static final String BROWSER_FRAGMENT_TAG = "browser";
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        Log.d("MainActivity", "-------------------------------------------------------");
+
+        // If we are starting afresh, start at the app list.
+        final FragmentManager fm = getSupportFragmentManager();
+        if (fm.findFragmentById(android.R.id.content) == null) {
+            fm.beginTransaction().add(android.R.id.content, new AppListFragment()).commit();
+        }
+    }
+}
+
diff --git a/tests/MusicServiceDemo/Android.mk b/tests/MusicServiceDemo/Android.mk
new file mode 100644
index 0000000..feef67a
--- /dev/null
+++ b/tests/MusicServiceDemo/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2013 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := MusicServiceDemo
+#LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-appcompat
+
+LOCAL_RESOURCE_DIR := \
+        $(LOCAL_PATH)/res \
+        frameworks/support/v7/appcompat/res
+LOCAL_PROGUARD_ENABLED := disabled
+#LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_AAPT_FLAGS := \
+        --auto-add-overlay \
+        --extra-packages android.support.v7.appcompat
+include $(BUILD_PACKAGE)
diff --git a/tests/MusicServiceDemo/AndroidManifest.xml b/tests/MusicServiceDemo/AndroidManifest.xml
new file mode 100644
index 0000000..4178a80
--- /dev/null
+++ b/tests/MusicServiceDemo/AndroidManifest.xml
@@ -0,0 +1,55 @@
+<?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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.musicservicedemo"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <uses-sdk
+        android:minSdkVersion="9"
+        android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme"
+        >
+
+        <activity
+            android:name="com.example.android.automotive.musicplayer.MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <service
+            android:name=".BrowserService"
+            android:exported="true"
+            >
+            <intent-filter>
+                <action android:name="android.media.browse.MediaBrowseService" />
+            </intent-filter>
+        </service>
+    </application>
+
+</manifest>
diff --git a/tests/MusicServiceDemo/proguard-project.txt b/tests/MusicServiceDemo/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/tests/MusicServiceDemo/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..47d6854
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..01b53fd
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..af762f2
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..eef47aa
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png b/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png
new file mode 100644
index 0000000..ea98c95
--- /dev/null
+++ b/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png
Binary files differ
diff --git a/tests/MusicServiceDemo/res/layout/activity_main.xml b/tests/MusicServiceDemo/res/layout/activity_main.xml
new file mode 100644
index 0000000..71753e3
--- /dev/null
+++ b/tests/MusicServiceDemo/res/layout/activity_main.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.example.android.automotive.musicplayer.MainActivity"
+    tools:ignore="MergeRootFrame" />
diff --git a/tests/MusicServiceDemo/res/layout/fragment_main.xml b/tests/MusicServiceDemo/res/layout/fragment_main.xml
new file mode 100644
index 0000000..8796e86
--- /dev/null
+++ b/tests/MusicServiceDemo/res/layout/fragment_main.xml
@@ -0,0 +1,32 @@
+<?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.
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.example.android.automotive.musicplayer.MainActivity$PlaceholderFragment" >
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/app_name" />
+
+</RelativeLayout>
diff --git a/tests/MusicServiceDemo/res/values/colors.xml b/tests/MusicServiceDemo/res/values/colors.xml
new file mode 100644
index 0000000..44dd05d
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/colors.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.
+-->
+
+<resources>
+    <color name="yellow">#ffffff00</color>
+    <color name="green">#ff00ff00</color>
+    <color name="blue">#ff0000ff</color>
+    <color name="red">#ffff0000</color>
+</resources>
diff --git a/tests/MusicServiceDemo/res/values/dimens.xml b/tests/MusicServiceDemo/res/values/dimens.xml
new file mode 100644
index 0000000..9f63ef2
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/tests/MusicServiceDemo/res/values/strings.xml b/tests/MusicServiceDemo/res/values/strings.xml
new file mode 100644
index 0000000..14c0171
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+<resources>
+
+    <string name="app_name">Music Service Demo</string>
+    <string name="action_settings">Settings</string>
+    <string name="thumbs_up">Thumbs Up</string>
+    <string name="music_error">No music found</string>
+    <string name="now_playing">Now Playing</string>
+
+</resources>
diff --git a/tests/MusicServiceDemo/res/values/styles.xml b/tests/MusicServiceDemo/res/values/styles.xml
new file mode 100644
index 0000000..b83662d
--- /dev/null
+++ b/tests/MusicServiceDemo/res/values/styles.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
new file mode 100644
index 0000000..9ca156f
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo;
+
+import android.app.SearchManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.content.res.Resources.NotFoundException;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.browse.MediaBrowserItem;
+import android.media.browse.MediaBrowserService;
+import android.media.session.MediaSession;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.example.android.musicservicedemo.browser.MusicProvider;
+import com.example.android.musicservicedemo.browser.MusicProviderTask;
+import com.example.android.musicservicedemo.browser.MusicProviderTaskListener;
+import com.example.android.musicservicedemo.browser.MusicTrack;
+
+import org.json.JSONException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service that implements MediaBrowserService and returns our menu hierarchy.
+ */
+public class BrowserService extends MediaBrowserService {
+    private static final String TAG = "BrowserService";
+
+    // URI paths for browsing music
+    public static final String BROWSE_ROOT_BASE_PATH = "browse";
+    public static final String NOW_PLAYING_PATH = "now_playing";
+    public static final String PIANO_BASE_PATH = "piano";
+    public static final String VOICE_BASE_PATH = "voice";
+
+    // Content URIs
+    public static final String AUTHORITY = "com.example.android.automotive.musicplayer";
+    public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
+    public static final Uri BROWSE_URI = Uri.withAppendedPath(BASE_URI, BROWSE_ROOT_BASE_PATH);
+
+    // URI matcher constants for browsing paths
+    public static final int BROWSE_ROOT = 1;
+    public static final int NOW_PLAYING = 2;
+    public static final int PIANO = 3;
+    public static final int VOICE = 4;
+
+    // Map the the URI paths with the URI matcher constants
+    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+    static {
+        sUriMatcher.addURI(AUTHORITY, BROWSE_ROOT_BASE_PATH, BROWSE_ROOT);
+        sUriMatcher.addURI(AUTHORITY, NOW_PLAYING_PATH, NOW_PLAYING);
+        sUriMatcher.addURI(AUTHORITY, PIANO_BASE_PATH, PIANO);
+        sUriMatcher.addURI(AUTHORITY, VOICE_BASE_PATH, VOICE);
+    }
+
+    // Media metadata that will be provided for a media container
+    public static final String[] MEDIA_CONTAINER_PROJECTION = {
+            "uri",
+            "title",
+            "subtitle",
+            "image_uri",
+            "supported_actions"
+    };
+
+    // MusicProvider will download the music catalog
+    private MusicProvider mMusicProvider;
+
+    private MediaSession mSession;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mSession = new MediaSession(this, "com.example.android.musicservicedemo.BrowserService");
+        setSessionToken(mSession.getSessionToken());
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public Uri onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+        return BROWSE_URI;
+    }
+
+    @Override
+    public List<MediaBrowserItem> onLoadChildren(Uri parentUri) {
+        final ArrayList<MediaBrowserItem> results = new ArrayList();
+
+        for (int i=0; i<10; i++) {
+            results.add(new MediaBrowserItem.Builder(Uri.withAppendedPath(BASE_URI, Integer.toString(i)),
+                    MediaBrowserItem.FLAG_BROWSABLE, "Title " + i).setSummary("Summary " + i).build());
+        }
+
+        return results;
+    }
+
+    @Override
+    public Bitmap onGetThumbnail(Uri uri, int width, int height, int density) {
+        return null;
+    }
+
+    /*
+    @Override
+    public void query(final Query query, final IMetadataResultHandler metadataResultHandler,
+            final IErrorHandler errorHandler)
+            throws RemoteException {
+        Log.d(TAG, "query: " + query);
+        Utils.checkNotNull(query);
+        Utils.checkNotNull(metadataResultHandler);
+        Utils.checkNotNull(errorHandler);
+
+        // Handle async response
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    // Pre-load the list of music
+                    List<MusicTrack> musicTracks = getMusicList();
+                    if (musicTracks == null) {
+                        notifyListenersOnPlaybackStateUpdate(getCurrentPlaybackState());
+                        errorHandler.onError(new Error(Error.UNKNOWN,
+                                getString(R.string.music_error)));
+                        return;
+                    }
+
+                    final Uri uri = query.getUri();
+                    int match = sUriMatcher.match(uri);
+                    Log.d(TAG, "Queried: " + uri + "; match: " + match);
+                    switch (match) {
+                        case BROWSE_ROOT:
+                        {
+                            Log.d(TAG, "Browse_root");
+
+                            try {
+                                MatrixCursor matrixCursor = mMusicProvider
+                                        .getRootContainerCurser();
+                                DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+                                        matrixCursor, null);
+
+                                Log.d(TAG, "on metadata response called " + holder.getCount());
+                                metadataResultHandler.onMetadataResponse(holder);
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Error delivering metadata in the callback.", e);
+                            }
+                            break;
+                        }
+                        case NOW_PLAYING:
+                        {
+                            try {
+                                Log.d(TAG, "query NOW_PLAYING");
+                                MatrixCursor matrixCursor = mMusicProvider
+                                        .getRootItemCursor(
+                                        PIANO);
+                                DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+                                        matrixCursor, null);
+                                Log.d(TAG, "on metadata response called " + holder.getCount());
+                                metadataResultHandler.onMetadataResponse(holder);
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Error querying NOW_PLAYING");
+                            }
+                            break;
+                        }
+                        case PIANO:
+                        {
+                            try {
+                                Log.d(TAG, "query PIANO");
+                                MatrixCursor matrixCursor = mMusicProvider
+                                        .getRootItemCursor(
+                                        PIANO);
+                                DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+                                        matrixCursor, null);
+                                Log.d(TAG, "on metadata response called " + holder.getCount());
+                                metadataResultHandler.onMetadataResponse(holder);
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Error querying PIANO");
+                            }
+                            break;
+                        }
+                        case VOICE:
+                        {
+                            try {
+                                Log.d(TAG, "query VOICE");
+                                MatrixCursor matrixCursor = mMusicProvider
+                                        .getRootItemCursor(
+                                        VOICE);
+                                DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION,
+                                        matrixCursor, null);
+                                Log.d(TAG, "on metadata response called " + holder.getCount());
+                                metadataResultHandler.onMetadataResponse(holder);
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Error querying VOICE");
+                            }
+                            break;
+                        }
+                        default:
+                        {
+                            Log.w(TAG, "Skipping unmatched URI: " + uri);
+                        }
+                    }
+                } catch (NotFoundException e) {
+                    Log.e(TAG, "::run:", e);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "::run:", e);
+                }
+            } // end run
+        }).start();
+    }
+
+    */
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java
new file mode 100644
index 0000000..db45b9d
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java
@@ -0,0 +1,101 @@
+/* Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBarActivity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.android.musicservicedemo.R;
+
+// TODO Local UI
+
+/**
+ * Main activity of the app.
+ */
+public class MainActivity extends ActionBarActivity {
+
+    private static final String LOG = "MainActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        if (savedInstanceState == null) {
+            getSupportFragmentManager().beginTransaction()
+                    .add(R.id.container, new PlaceholderFragment())
+                    .commit();
+        }
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
+     */
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+
+        // Inflate the menu; this adds items to the action bar if it is present.
+        //getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+        // if (id == R.id.action_settings) {
+        // return true;
+        // }
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * A placeholder fragment containing a simple view.
+     */
+    public static class PlaceholderFragment extends Fragment {
+
+        public PlaceholderFragment() {
+        }
+
+        /*
+         * (non-Javadoc)
+         * @see
+         * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater
+         * , android.view.ViewGroup, android.os.Bundle)
+         */
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
+            return rootView;
+        }
+    }
+
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java
new file mode 100644
index 0000000..3589761
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class Utils {
+
+    private static final String TAG = "Utils";
+
+    /**
+     * Utility method to check that parameters are not null
+     *
+     * @param object
+     */
+    public static final void checkNotNull(Object object) {
+        if (object == null) {
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+     * Utility to download a bitmap
+     *
+     * @param source
+     * @return
+     */
+    public static Bitmap getBitmapFromURL(String source) {
+        try {
+            URL url = new URL(source);
+            HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
+            httpConnection.setDoInput(true);
+            httpConnection.connect();
+            InputStream inputStream = httpConnection.getInputStream();
+            return BitmapFactory.decodeStream(inputStream);
+        } catch (IOException e) {
+            Log.e(TAG, "getBitmapFromUrl: " + source, e);
+        }
+        return null;
+    }
+
+    /**
+     * Utility method to wrap an index
+     *
+     * @param i
+     * @param size
+     * @return
+     */
+    public static int wrapIndex(int i, int size) {
+        int m = i % size;
+        if (m < 0) { // java modulus can be negative
+            m += size;
+        }
+        return m;
+    }
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java
new file mode 100644
index 0000000..15038d7
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo.browser;
+
+import android.database.MatrixCursor;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.util.Log;
+
+import com.example.android.musicservicedemo.BrowserService;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to get a list of MusicTrack's based on a server-side JSON
+ * configuration.
+ */
+public class MusicProvider {
+
+    private static final String TAG = "MusicProvider";
+
+    private static final String MUSIC_URL = "http://storage.googleapis.com/automotive-media/music.json";
+
+    private static String MUSIC = "music";
+    private static String TITLE = "title";
+    private static String ALBUM = "album";
+    private static String ARTIST = "artist";
+    private static String GENRE = "genre";
+    private static String SOURCE = "source";
+    private static String IMAGE = "image";
+    private static String TRACK_NUMBER = "trackNumber";
+    private static String TOTAL_TRACK_COUNT = "totalTrackCount";
+    private static String DURATION = "duration";
+
+    // Cache for music track data
+    private static List<MusicTrack> mMusicList;
+
+    /**
+     * Get the cached list of music tracks
+     *
+     * @return
+     * @throws JSONException
+     */
+    public List<MusicTrack> getMedia() throws JSONException {
+        if (null != mMusicList && mMusicList.size() > 0) {
+            return mMusicList;
+        }
+        return null;
+    }
+
+    /**
+     * Get the list of music tracks from a server and return the list of
+     * MusicTrack objects.
+     *
+     * @return
+     * @throws JSONException
+     */
+    public List<MusicTrack> retreiveMedia() throws JSONException {
+        if (null != mMusicList) {
+            return mMusicList;
+        }
+        int slashPos = MUSIC_URL.lastIndexOf('/');
+        String path = MUSIC_URL.substring(0, slashPos + 1);
+        JSONObject jsonObj = parseUrl(MUSIC_URL);
+
+        try {
+            JSONArray videos = jsonObj.getJSONArray(MUSIC);
+            if (null != videos) {
+                mMusicList = new ArrayList<MusicTrack>();
+                for (int j = 0; j < videos.length(); j++) {
+                    JSONObject music = videos.getJSONObject(j);
+                    String title = music.getString(TITLE);
+                    String album = music.getString(ALBUM);
+                    String artist = music.getString(ARTIST);
+                    String genre = music.getString(GENRE);
+                    String source = music.getString(SOURCE);
+                    // Media is stored relative to JSON file
+                    if (!source.startsWith("http")) {
+                        source = path + source;
+                    }
+                    String image = music.getString(IMAGE);
+                    if (!image.startsWith("http")) {
+                        image = path + image;
+                    }
+                    int trackNumber = music.getInt(TRACK_NUMBER);
+                    int totalTrackCount = music.getInt(TOTAL_TRACK_COUNT);
+                    int duration = music.getInt(DURATION) * 1000; // ms
+
+                    mMusicList.add(new MusicTrack(title, album, artist, genre, source,
+                            image, trackNumber, totalTrackCount, duration));
+                }
+            }
+        } catch (NullPointerException e) {
+            Log.e(TAG, "retreiveMedia", e);
+        }
+        return mMusicList;
+    }
+
+    /**
+     * Download a JSON file from a server, parse the content and return the JSON
+     * object.
+     *
+     * @param urlString
+     * @return
+     */
+    private JSONObject parseUrl(String urlString) {
+        InputStream is = null;
+        try {
+            java.net.URL url = new java.net.URL(urlString);
+            URLConnection urlConnection = url.openConnection();
+            is = new BufferedInputStream(urlConnection.getInputStream());
+            BufferedReader reader = new BufferedReader(new InputStreamReader(
+                    urlConnection.getInputStream(), "iso-8859-1"), 8);
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+            }
+            return new JSONObject(sb.toString());
+        } catch (Exception e) {
+            Log.d(TAG, "Failed to parse the json for media list", e);
+            return null;
+        } finally {
+            if (null != is) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    public MatrixCursor getRootContainerCurser() {
+        MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+        Uri.Builder pianoBuilder = new Uri.Builder();
+        pianoBuilder.authority(BrowserService.AUTHORITY);
+        pianoBuilder.appendPath(BrowserService.PIANO_BASE_PATH);
+        matrixCursor.addRow(new Object[] {
+                pianoBuilder.build(),
+                BrowserService.PIANO_BASE_PATH,
+                "subtitle",
+                null,
+                0
+        });
+
+        Uri.Builder voiceBuilder = new Uri.Builder();
+        voiceBuilder.authority(BrowserService.AUTHORITY);
+        voiceBuilder.appendPath(BrowserService.VOICE_BASE_PATH);
+        matrixCursor.addRow(new Object[] {
+                voiceBuilder.build(),
+                BrowserService.VOICE_BASE_PATH,
+                "subtitle",
+                null,
+                0
+        });
+        return matrixCursor;
+    }
+
+    public MatrixCursor getRootItemCursor(int type) {
+        if (type == BrowserService.NOW_PLAYING) {
+            MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+
+            try {
+                // Just return all of the tracks for now
+                List<MusicTrack> musicTracks = retreiveMedia();
+                for (MusicTrack musicTrack : musicTracks) {
+                    Uri.Builder builder = new Uri.Builder();
+                    builder.authority(BrowserService.AUTHORITY);
+                    builder.appendPath(BrowserService.NOW_PLAYING_PATH);
+                    builder.appendPath(musicTrack.getTitle());
+                    matrixCursor.addRow(new Object[] {
+                            builder.build(),
+                            musicTrack.getTitle(),
+                            musicTrack.getArtist(),
+                            musicTrack.getImage(),
+                            PlaybackState.ACTION_PLAY
+                    });
+                    Log.d(TAG, "Uri " + builder.build());
+                }
+            } catch (JSONException e) {
+                Log.e(TAG, "::getRootItemCursor:", e);
+            }
+
+            Log.d(TAG, "cursor: " + matrixCursor.getCount());
+            return matrixCursor;
+        } else if (type == BrowserService.PIANO) {
+            MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+
+            try {
+                List<MusicTrack> musicTracks = retreiveMedia();
+                for (MusicTrack musicTrack : musicTracks) {
+                    Uri.Builder builder = new Uri.Builder();
+                    builder.authority(BrowserService.AUTHORITY);
+                    builder.appendPath(BrowserService.PIANO_BASE_PATH);
+                    builder.appendPath(musicTrack.getTitle());
+                    matrixCursor.addRow(new Object[] {
+                            builder.build(),
+                            musicTrack.getTitle(),
+                            musicTrack.getArtist(),
+                            musicTrack.getImage(),
+                            PlaybackState.ACTION_PLAY
+                    });
+                    Log.d(TAG, "Uri " + builder.build());
+                }
+            } catch (JSONException e) {
+                Log.e(TAG, "::getRootItemCursor:", e);
+            }
+
+            Log.d(TAG, "cursor: " + matrixCursor.getCount());
+            return matrixCursor;
+        } else if (type == BrowserService.VOICE) {
+            MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION);
+
+            try {
+                List<MusicTrack> musicTracks = retreiveMedia();
+                for (MusicTrack musicTrack : musicTracks) {
+                    Uri.Builder builder = new Uri.Builder();
+                    builder.authority(BrowserService.AUTHORITY);
+                    builder.appendPath(BrowserService.VOICE_BASE_PATH);
+                    builder.appendPath(musicTrack.getTitle());
+                    matrixCursor.addRow(new Object[] {
+                            builder.build(),
+                            musicTrack.getTitle(),
+                            musicTrack.getArtist(),
+                            musicTrack.getImage(),
+                            PlaybackState.ACTION_PLAY
+                    });
+                    Log.d(TAG, "Uri " + builder.build());
+                }
+            } catch (JSONException e) {
+                Log.e(TAG, "::getRootItemCursor:", e);
+            }
+
+            Log.d(TAG, "cursor: " + matrixCursor.getCount());
+            return matrixCursor;
+
+        }
+        return null;
+    }
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java
new file mode 100644
index 0000000..ffda110
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java
@@ -0,0 +1,70 @@
+/*  
+ * 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.example.android.musicservicedemo.browser;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import org.json.JSONException;
+
+/**
+ * Asynchronous task to retrieve the music data using MusicProvider.
+ */
+public class MusicProviderTask extends AsyncTask<Void, Void, Void> {
+
+    private static final String TAG = "MusicProviderTask";
+
+    MusicProvider mMusicProvider;
+    MusicProviderTaskListener mMusicProviderTaskListener;
+
+    /**
+     * Initialize the task with the provider to download the music data and the
+     * listener to be informed when the task is done.
+     *
+     * @param musicProvider
+     * @param listener
+     */
+    public MusicProviderTask(MusicProvider musicProvider,
+            MusicProviderTaskListener listener) {
+        mMusicProvider = musicProvider;
+        mMusicProviderTaskListener = listener;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.os.AsyncTask#doInBackground(java.lang.Object[])
+     */
+    @Override
+    protected Void doInBackground(Void... arg0) {
+        try {
+            mMusicProvider.retreiveMedia();
+        } catch (JSONException e) {
+            Log.e(TAG, "::doInBackground:", e);
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
+     */
+    @Override
+    protected void onPostExecute(Void result) {
+        mMusicProviderTaskListener.onMusicProviderTaskCompleted();
+    }
+
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java
new file mode 100644
index 0000000..b1d168f
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo.browser;
+
+/**
+ * Callback listener for completion of MusicProviderTask
+ */
+public interface MusicProviderTaskListener {
+    public void onMusicProviderTaskCompleted();
+}
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java
new file mode 100644
index 0000000..02ea899
--- /dev/null
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.musicservicedemo.browser;
+
+/**
+ * A class to model music track metadata.
+ */
+public class MusicTrack {
+
+    private static final String TAG = "MusicTrack";
+
+    private String mTitle;
+    private String mAlbum;
+    private String mArtist;
+    private String mGenre;
+    private String mSource;
+    private String mImage;
+    private int mTrackNumber;
+    private int mTotalTrackCount;
+    private int mDuration;
+
+    /**
+     * Constructor creating a MusicTrack instance.
+     *
+     * @param title
+     * @param album
+     * @param artist
+     * @param genre
+     * @param source
+     * @param image
+     * @param trackNumber
+     * @param totalTrackCount
+     * @param duration
+     */
+    public MusicTrack(String title, String album, String artist, String genre, String source,
+            String image, int trackNumber, int totalTrackCount, int duration) {
+        this.mTitle = title;
+        this.mAlbum = album;
+        this.mArtist = artist;
+        this.mGenre = genre;
+        this.mSource = source;
+        this.mImage = image;
+        this.mTrackNumber = trackNumber;
+        this.mTotalTrackCount = totalTrackCount;
+        this.mDuration = duration;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public void setTitle(String mTitle) {
+        this.mTitle = mTitle;
+    }
+
+    public String getAlbum() {
+        return mAlbum;
+    }
+
+    public void setAlbum(String mAlbum) {
+        this.mAlbum = mAlbum;
+    }
+
+    public String getArtist() {
+        return mArtist;
+    }
+
+    public void setArtist(String mArtist) {
+        this.mArtist = mArtist;
+    }
+
+    public String getGenre() {
+        return mGenre;
+    }
+
+    public void setGenre(String mGenre) {
+        this.mGenre = mGenre;
+    }
+
+    public String getSource() {
+        return mSource;
+    }
+
+    public void setSource(String mSource) {
+        this.mSource = mSource;
+    }
+
+    public String getImage() {
+        return mImage;
+    }
+
+    public void setImage(String mImage) {
+        this.mImage = mImage;
+    }
+
+    public int getTrackNumber() {
+        return mTrackNumber;
+    }
+
+    public void setTrackNumber(int mTrackNumber) {
+        this.mTrackNumber = mTrackNumber;
+    }
+
+    public int getTotalTrackCount() {
+        return mTotalTrackCount;
+    }
+
+    public void setTotalTrackCount(int mTotalTrackCount) {
+        this.mTotalTrackCount = mTotalTrackCount;
+    }
+
+    public int getDuration() {
+        return mDuration;
+    }
+
+    public void setDuration(int mDuration) {
+        this.mDuration = mDuration;
+    }
+
+    public String toString() {
+        return mTitle;
+    }
+
+}
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index 33f000d..c328b3c 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -1,6 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.test.voiceinteraction">
 
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
     <application>
         <activity android:name="VoiceInteractionMain" android:label="Voice Interaction"
                 android:theme="@android:style/Theme.Material">
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index ead3b13..9300401 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -27,8 +27,8 @@
 # We need to process the framework classes.jar file, but we can't
 # depend directly on it (private vars won't be inherited correctly).
 # So, we depend on framework's BUILT file.
-built_framework_dep := $(call java-lib-deps,framework-base)
-built_framework_classes := $(call java-lib-files,framework-base)
+built_framework_dep := $(call java-lib-deps,framework)
+built_framework_classes := $(call java-lib-files,framework)
 
 built_core_dep := $(call java-lib-deps,core-libart)
 built_core_classes := $(call java-lib-files,core-libart)
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 7153a90..776398f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -608,6 +608,9 @@
      * @param y The y-coordinate of the end of a line
      */
     private void lineTo(float x, float y) {
+        if (isEmpty()) {
+            mPath.moveTo(mLastX = 0, mLastY = 0);
+        }
         mPath.lineTo(mLastX = x, mLastY = y);
     }