Merge "audio: fixed channel count determination from channel mask"
diff --git a/api/current.txt b/api/current.txt
index f9ba781..8548e2f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3212,7 +3212,6 @@
     ctor public ActionBar.LayoutParams(int);
     ctor public ActionBar.LayoutParams(android.app.ActionBar.LayoutParams);
     ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
-    ctor public ActionBar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
     field public int gravity;
   }
 
@@ -12567,6 +12566,7 @@
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
     field public static final int LENS_FACING_BACK = 1; // 0x1
     field public static final int LENS_FACING_FRONT = 0; // 0x0
@@ -13819,6 +13819,7 @@
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
     method public boolean isSpeakerphoneOn();
+    method public boolean isVolumeFixed();
     method public deprecated boolean isWiredHeadsetOn();
     method public void loadSoundEffects();
     method public void playSoundEffect(int);
@@ -21703,6 +21704,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+    field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
     field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
     field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
@@ -23223,6 +23225,7 @@
     field public static final android.net.Uri AUTHORITY_URI;
     field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
     field public static final java.lang.String DEFERRED_SNIPPETING = "deferred_snippeting";
+    field public static final java.lang.String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
     field public static final java.lang.String DIRECTORY_PARAM_KEY = "directory";
     field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
     field public static final java.lang.String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
@@ -23888,11 +23891,10 @@
     field public static final android.net.Uri PROFILE_CONTENT_URI;
   }
 
-  public static class ContactsContract.SearchSnippetColumns {
-    ctor public ContactsContract.SearchSnippetColumns();
+  public static class ContactsContract.SearchSnippets {
+    ctor public ContactsContract.SearchSnippets();
     field public static final java.lang.String DEFERRED_SNIPPETING_KEY = "deferred_snippeting";
     field public static final java.lang.String SNIPPET = "snippet";
-    field public static final java.lang.String SNIPPET_ARGS_PARAM_KEY = "snippet_args";
   }
 
   public static final class ContactsContract.Settings implements android.provider.ContactsContract.SettingsColumns {
@@ -26578,13 +26580,15 @@
 
   public final class SynthesisRequest {
     ctor public SynthesisRequest(java.lang.String, android.os.Bundle);
+    ctor public SynthesisRequest(java.lang.CharSequence, android.os.Bundle);
     method public int getCallerUid();
+    method public java.lang.CharSequence getCharSequenceText();
     method public java.lang.String getCountry();
     method public java.lang.String getLanguage();
     method public android.os.Bundle getParams();
     method public int getPitch();
     method public int getSpeechRate();
-    method public java.lang.String getText();
+    method public deprecated java.lang.String getText();
     method public java.lang.String getVariant();
   }
 
@@ -26594,7 +26598,9 @@
     method public int addEarcon(java.lang.String, java.lang.String, int);
     method public int addEarcon(java.lang.String, java.lang.String);
     method public int addSpeech(java.lang.String, java.lang.String, int);
+    method public int addSpeech(java.lang.CharSequence, java.lang.String, int);
     method public int addSpeech(java.lang.String, java.lang.String);
+    method public int addSpeech(java.lang.CharSequence, java.lang.String);
     method public boolean areDefaultsEnforced();
     method public java.lang.String getDefaultEngine();
     method public java.util.Locale getDefaultLanguage();
@@ -26604,8 +26610,10 @@
     method public static int getMaxSpeechInputLength();
     method public int isLanguageAvailable(java.util.Locale);
     method public boolean isSpeaking();
-    method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
-    method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public deprecated int setEngineByPackageName(java.lang.String);
     method public int setLanguage(java.util.Locale);
     method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
@@ -26613,9 +26621,11 @@
     method public int setPitch(float);
     method public int setSpeechRate(float);
     method public void shutdown();
-    method public int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public int speak(java.lang.CharSequence, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public deprecated int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public int stop();
-    method public int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+    method public int synthesizeToFile(java.lang.CharSequence, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String, java.lang.String);
+    method public deprecated int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
     field public static final java.lang.String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_INVALID_REQUEST = -8; // 0xfffffff8
@@ -26659,6 +26669,7 @@
     field public static final java.lang.String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
     field public static final java.lang.String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
     field public static final java.lang.String KEY_PARAM_PAN = "pan";
+    field public static final java.lang.String KEY_PARAM_SESSION_ID = "sessionId";
     field public static final java.lang.String KEY_PARAM_STREAM = "streamType";
     field public static final java.lang.String KEY_PARAM_UTTERANCE_ID = "utteranceId";
     field public static final java.lang.String KEY_PARAM_VOLUME = "volume";
@@ -27451,6 +27462,12 @@
     field public static final int SWAP_CALLS = 8; // 0x8
   }
 
+  public final class CallFeatures {
+    field public static final int NONE = 0; // 0x0
+    field public static final int VoLTE = 1; // 0x1
+    field public static final int VoWIFI = 2; // 0x2
+  }
+
   public final class CallInfo implements android.os.Parcelable {
     ctor public CallInfo(java.lang.String, android.telecomm.CallState, android.net.Uri);
     method public int describeContents();
@@ -27487,7 +27504,9 @@
     method protected void onAdapterAttached(android.telecomm.CallServiceAdapter);
     method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract void onFeaturesChanged(java.lang.String, int);
     method public void onPostDialContinue(java.lang.String, boolean);
+    method public void onPostDialWait(android.telecomm.Connection, java.lang.String);
     method public abstract void playDtmfTone(java.lang.String, char);
     method public abstract void reject(java.lang.String);
     method public abstract void setIncomingCallId(java.lang.String, android.os.Bundle);
@@ -27508,6 +27527,7 @@
     method public void setCallVideoProvider(java.lang.String, android.telecomm.CallVideoProvider);
     method public void setDialing(java.lang.String);
     method public void setDisconnected(java.lang.String, int, java.lang.String);
+    method public void setFeatures(java.lang.String, int);
     method public void setOnHold(java.lang.String);
     method public void setRequestingRingback(java.lang.String, boolean);
     method public void setRinging(java.lang.String);
@@ -27557,12 +27577,12 @@
 
   public abstract class CallVideoClient {
     ctor protected CallVideoClient();
-    method public abstract void onCallSessionEvent(int);
-    method public abstract void onCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
+    method public abstract void onHandleCallSessionEvent(int);
+    method public abstract void onHandleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
     method public abstract void onReceiveSessionModifyRequest(android.telecomm.VideoCallProfile);
     method public abstract void onReceiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile);
     method public abstract void onUpdateCallDataUsage(int);
-    method public abstract void onUpdatedPeerDimensions(int, int);
+    method public abstract void onUpdatePeerDimensions(int, int);
     field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
     field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
     field public static final int SESSION_EVENT_TX_START = 3; // 0x3
@@ -27574,22 +27594,24 @@
 
   public abstract class CallVideoProvider {
     ctor protected CallVideoProvider();
-    method public abstract void requestCallDataUsage();
-    method public abstract void requestCameraCapabilities();
-    method public abstract void sendSessionModifyRequest(android.telecomm.VideoCallProfile);
-    method public abstract void sendSessionModifyResponse(android.telecomm.VideoCallProfile);
-    method public abstract void setCamera(java.lang.String);
-    method public abstract void setDeviceOrientation(int);
-    method public abstract void setDisplaySurface(android.view.Surface);
-    method public abstract void setPauseImage(java.lang.String);
-    method public abstract void setPreviewSurface(android.view.Surface);
-    method public abstract void setZoom(float);
+    method public abstract void onRequestCallDataUsage();
+    method public abstract void onRequestCameraCapabilities();
+    method public abstract void onSendSessionModifyRequest(android.telecomm.VideoCallProfile);
+    method public abstract void onSendSessionModifyResponse(android.telecomm.VideoCallProfile);
+    method public abstract void onSetCallVideoClient(android.telecomm.RemoteCallVideoClient);
+    method public abstract void onSetCamera(java.lang.String);
+    method public abstract void onSetDeviceOrientation(int);
+    method public abstract void onSetDisplaySurface(android.view.Surface);
+    method public abstract void onSetPauseImage(java.lang.String);
+    method public abstract void onSetPreviewSurface(android.view.Surface);
+    method public abstract void onSetZoom(float);
   }
 
   public abstract class Connection {
     ctor protected Connection();
     method public final android.telecomm.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecomm.Connection> getChildConnections();
+    method public final int getFeatures();
     method public final android.net.Uri getHandle();
     method public final android.telecomm.Connection getParentConnection();
     method public final int getState();
@@ -27615,6 +27637,7 @@
     method public final void setDestroyed();
     method public final void setDialing();
     method public final void setDisconnected(int, java.lang.String);
+    method public final void setFeatures(int);
     method public final void setHandle(android.net.Uri);
     method public final void setIsConferenceCapable(boolean);
     method public final void setOnHold();
@@ -27665,6 +27688,7 @@
     method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
     method protected void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.Connection>);
     method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
+    method public final void onFeaturesChanged(java.lang.String, int);
     method public final void playDtmfTone(java.lang.String, char);
     method public final void reject(java.lang.String);
     method public final void setIncomingCallId(java.lang.String, android.os.Bundle);
@@ -27711,6 +27735,7 @@
     method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
     method public int getDisconnectCauseCode();
     method public java.lang.String getDisconnectCauseMsg();
+    method public int getFeatures();
     method public android.telecomm.GatewayInfo getGatewayInfo();
     method public android.net.Uri getHandle();
     method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor();
@@ -27734,9 +27759,29 @@
     method protected abstract void updateCall(android.telecomm.InCallCall);
   }
 
+  public class RemoteCallVideoClient implements android.os.IBinder.DeathRecipient {
+    method public void binderDied();
+    method public void handleCallSessionEvent(int) throws android.os.RemoteException;
+    method public void handleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities) throws android.os.RemoteException;
+    method public void receiveSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+    method public void receiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+    method public void updateCallDataUsage(int) throws android.os.RemoteException;
+    method public void updatePeerDimensions(int, int) throws android.os.RemoteException;
+  }
+
   public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient {
     method public void binderDied();
+    method public void requestCallDataUsage() throws android.os.RemoteException;
+    method public void requestCameraCapabilities() throws android.os.RemoteException;
+    method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+    method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+    method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException;
     method public void setCamera(java.lang.String) throws android.os.RemoteException;
+    method public void setDeviceOrientation(int) throws android.os.RemoteException;
+    method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException;
+    method public void setPauseImage(java.lang.String) throws android.os.RemoteException;
+    method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException;
+    method public void setZoom(float) throws android.os.RemoteException;
   }
 
   public final class RemoteConnection {
@@ -27746,6 +27791,7 @@
     method public void disconnect();
     method public int getDisconnectCause();
     method public java.lang.String getDisconnectMessage();
+    method public int getState();
     method public void hold();
     method public void playDtmf(char);
     method public void postDialContinue(boolean);
@@ -32264,6 +32310,7 @@
     method public final float getY();
     method public final float getY(int);
     method public final float getYPrecision();
+    method public final boolean isButtonPressed(int);
     method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
     method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
     method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -33845,6 +33892,7 @@
     field public static final int FEATURE_RIGHT_ICON = 4; // 0x4
     field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
     field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002
+    field public static final java.lang.String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "android:navigation:background";
     field public static final int PROGRESS_END = 10000; // 0x2710
     field public static final int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
     field public static final int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
@@ -33853,6 +33901,7 @@
     field public static final int PROGRESS_START = 0; // 0x0
     field public static final int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
     field public static final int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
+    field public static final java.lang.String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
   }
 
   public static abstract interface Window.Callback {
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 628875f..5c98180 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -1334,8 +1334,14 @@
             super(source);
         }
 
-        public LayoutParams(MarginLayoutParams source) {
-            super(source);
-        }
+        /*
+         * Note for framework developers:
+         *
+         * You might notice that ActionBar.LayoutParams is missing a constructor overload
+         * for MarginLayoutParams. While it may seem like a good idea to add one, at this
+         * point it's dangerous for source compatibility. Upon building against a new
+         * version of the SDK an app can end up statically linking to the new MarginLayoutParams
+         * overload, causing a crash when running on older platform versions with no other changes.
+         */
     }
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 6b5549d..fafa948 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -124,6 +124,8 @@
     public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
     /** @hide */
     public static final int ANIM_SCENE_TRANSITION = 5;
+    /** @hide */
+    public static final int ANIM_DEFAULT = 6;
 
     private String mPackageName;
     private int mAnimationType = ANIM_NONE;
@@ -344,10 +346,9 @@
      * enabled on the calling Activity to cause an exit transition. The same must be in
      * the called Activity to get an entering transition.</p>
      * @param activity The Activity whose window contains the shared elements.
-     * @param sharedElement The View to transition to the started Activity. sharedElement must
-     *                      have a non-null sharedElementName.
-     * @param sharedElementName The shared element name as used in the target Activity. This may
-     *                          be null if it has the same name as sharedElement.
+     * @param sharedElement The View to transition to the started Activity.
+     * @param sharedElementName The shared element name as used in the target Activity. This
+     *                          must not be null.
      * @return Returns a new ActivityOptions object that you can use to
      *         supply these options as the options Bundle when starting an activity.
      * @see android.transition.Transition#setEpicenterCallback(
@@ -374,16 +375,16 @@
      *                       a unique shared element name.
      * @return Returns a new ActivityOptions object that you can use to
      *         supply these options as the options Bundle when starting an activity.
-     *         Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}.
      * @see android.transition.Transition#setEpicenterCallback(
      *          android.transition.Transition.EpicenterCallback)
      */
     public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
             Pair<View, String>... sharedElements) {
-        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
-            return null;
-        }
         ActivityOptions opts = new ActivityOptions();
+        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+            opts.mAnimationType = ANIM_DEFAULT;
+            return opts;
+        }
         opts.mAnimationType = ANIM_SCENE_TRANSITION;
 
         ArrayList<String> names = new ArrayList<String>();
@@ -577,9 +578,9 @@
         mResultData = null;
         mResultCode = 0;
         mExitCoordinatorIndex = 0;
+        mAnimationType = otherOptions.mAnimationType;
         switch (otherOptions.mAnimationType) {
             case ANIM_CUSTOM:
-                mAnimationType = otherOptions.mAnimationType;
                 mCustomEnterResId = otherOptions.mCustomEnterResId;
                 mCustomExitResId = otherOptions.mCustomExitResId;
                 mThumbnail = null;
@@ -592,7 +593,6 @@
                 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
                 break;
             case ANIM_SCALE_UP:
-                mAnimationType = otherOptions.mAnimationType;
                 mStartX = otherOptions.mStartX;
                 mStartY = otherOptions.mStartY;
                 mStartWidth = otherOptions.mStartWidth;
@@ -607,7 +607,6 @@
                 break;
             case ANIM_THUMBNAIL_SCALE_UP:
             case ANIM_THUMBNAIL_SCALE_DOWN:
-                mAnimationType = otherOptions.mAnimationType;
                 mThumbnail = otherOptions.mThumbnail;
                 mStartX = otherOptions.mStartX;
                 mStartY = otherOptions.mStartY;
@@ -620,7 +619,6 @@
                 mAnimationStartedListener = otherOptions.mAnimationStartedListener;
                 break;
             case ANIM_SCENE_TRANSITION:
-                mAnimationType = otherOptions.mAnimationType;
                 mTransitionReceiver = otherOptions.mTransitionReceiver;
                 mSharedElementNames = otherOptions.mSharedElementNames;
                 mIsReturning = otherOptions.mIsReturning;
@@ -642,6 +640,9 @@
      * methods that take an options Bundle.
      */
     public Bundle toBundle() {
+        if (mAnimationType == ANIM_DEFAULT) {
+            return null;
+        }
         Bundle b = new Bundle();
         if (mPackageName != null) {
             b.putString(KEY_PACKAGE_NAME, mPackageName);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a480219..b7e64a2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -191,7 +191,9 @@
     /** @hide */
     public static final int OP_MUTE_MICROPHONE = 44;
     /** @hide */
-    public static final int _NUM_OP = 45;
+    public static final int OP_TOAST_WINDOW = 45;
+    /** @hide */
+    public static final int _NUM_OP = 46;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION =
@@ -259,7 +261,8 @@
             OP_COARSE_LOCATION,
             OP_COARSE_LOCATION,
             OP_GET_USAGE_STATS,
-            OP_MUTE_MICROPHONE
+            OP_MUTE_MICROPHONE,
+            OP_TOAST_WINDOW,
     };
 
     /**
@@ -312,6 +315,7 @@
             OPSTR_MONITOR_HIGH_POWER_LOCATION,
             null,
             null,
+            null,
     };
 
     /**
@@ -364,6 +368,7 @@
             "MONITOR_HIGH_POWER_LOCATION",
             "GET_USAGE_STATS",
             "OP_MUTE_MICROPHONE",
+            "TOAST_WINDOW",
     };
 
     /**
@@ -416,6 +421,7 @@
             null, // no permission for high power location monitoring
             android.Manifest.permission.PACKAGE_USAGE_STATS,
             null, // no permission for muting/unmuting microphone
+            null, // no permission for displaying toasts
     };
 
     /**
@@ -448,7 +454,7 @@
             null, //READ_ICC_SMS
             null, //WRITE_ICC_SMS
             null, //WRITE_SETTINGS
-            null, //SYSTEM_ALERT_WINDOW
+            UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
             null, //ACCESS_NOTIFICATIONS
             null, //CAMERA
             null, //RECORD_AUDIO
@@ -469,6 +475,60 @@
             null, //MONITOR_HIGH_POWER_LOCATION
             null, //GET_USAGE_STATS
             UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
+            UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
+    };
+
+    /**
+     * This specifies whether each option should allow the system
+     * (and system ui) to bypass the user restriction when active.
+     */
+    private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] {
+            false, //COARSE_LOCATION
+            false, //FINE_LOCATION
+            false, //GPS
+            false, //VIBRATE
+            false, //READ_CONTACTS
+            false, //WRITE_CONTACTS
+            false, //READ_CALL_LOG
+            false, //WRITE_CALL_LOG
+            false, //READ_CALENDAR
+            false, //WRITE_CALENDAR
+            false, //WIFI_SCAN
+            false, //POST_NOTIFICATION
+            false, //NEIGHBORING_CELLS
+            false, //CALL_PHONE
+            false, //READ_SMS
+            false, //WRITE_SMS
+            false, //RECEIVE_SMS
+            false, //RECEIVE_EMERGECY_SMS
+            false, //RECEIVE_MMS
+            false, //RECEIVE_WAP_PUSH
+            false, //SEND_SMS
+            false, //READ_ICC_SMS
+            false, //WRITE_ICC_SMS
+            false, //WRITE_SETTINGS
+            true, //SYSTEM_ALERT_WINDOW
+            false, //ACCESS_NOTIFICATIONS
+            false, //CAMERA
+            false, //RECORD_AUDIO
+            false, //PLAY_AUDIO
+            false, //READ_CLIPBOARD
+            false, //WRITE_CLIPBOARD
+            false, //TAKE_MEDIA_BUTTONS
+            false, //TAKE_AUDIO_FOCUS
+            false, //AUDIO_MASTER_VOLUME
+            false, //AUDIO_VOICE_VOLUME
+            false, //AUDIO_RING_VOLUME
+            false, //AUDIO_MEDIA_VOLUME
+            false, //AUDIO_ALARM_VOLUME
+            false, //AUDIO_NOTIFICATION_VOLUME
+            false, //AUDIO_BLUETOOTH_VOLUME
+            false, //WAKE_LOCK
+            false, //MONITOR_LOCATION
+            false, //MONITOR_HIGH_POWER_LOCATION
+            false, //GET_USAGE_STATS
+            false, // MUTE_MICROPHONE
+            true, // TOAST_WINDOW
     };
 
     /**
@@ -520,6 +580,7 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
             AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
     };
 
     /**
@@ -575,6 +636,7 @@
             false,
             false,
             false,
+            false,
     };
 
     private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
@@ -608,6 +670,10 @@
             throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length
                     + " should be " + _NUM_OP);
         }
+        if (sOpAllowSystemRestrictionBypass.length != _NUM_OP) {
+            throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length "
+                    + sOpRestrictions.length + " should be " + _NUM_OP);
+        }
         for (int i=0; i<_NUM_OP; i++) {
             if (sOpToString[i] != null) {
                 sOpStrToOp.put(sOpToString[i], i);
@@ -649,6 +715,15 @@
     }
 
     /**
+     * Retrieve whether the op allows the system (and system ui) to
+     * bypass the user restriction.
+     * @hide
+     */
+    public static boolean opAllowSystemBypassRestriction(int op) {
+        return sOpAllowSystemRestrictionBypass[op];
+    }
+
+    /**
      * Retrieve the default mode for the operation.
      * @hide
      */
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cb48e58..e2f3c60 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -36,6 +36,7 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
@@ -49,6 +50,7 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Process;
@@ -69,6 +71,15 @@
     private final static boolean DEBUG = false;
     private final static boolean DEBUG_ICONS = false;
 
+    UserManager mUserManager;
+
+    UserManager getUserManager() {
+        if (mUserManager == null) {
+            mUserManager = UserManager.get(mContext);
+        }
+        return mUserManager;
+    }
+
     @Override
     public PackageInfo getPackageInfo(String packageName, int flags)
             throws NameNotFoundException {
@@ -1500,10 +1511,16 @@
     /**
      * @hide
      */
-    @Override
-    public Bitmap getUserIcon(int userId) {
-        UserManager um = UserManager.get(mContext);
-        return um.getUserIcon(userId);
+    public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
+        if (itemInfo.showUserIcon != UserHandle.USER_NULL) {
+            return new BitmapDrawable(getUserManager().getUserIcon(itemInfo.showUserIcon));
+        }
+        Drawable dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+        if (dr != null) {
+            dr = getUserManager().getBadgedDrawableForUser(dr,
+                    new UserHandle(mContext.getUserId()));
+        }
+        return dr;
     }
 
     private final ContextImpl mContext;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index d33b14a..5e18d0f 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -45,6 +45,7 @@
     private static final String TAG = "EnterTransitionCoordinator";
 
     private static final long MAX_WAIT_MS = 1000;
+    private static final int MIN_ANIMATION_FRAMES = 2;
 
     private boolean mSharedElementTransitionStarted;
     private Activity mActivity;
@@ -56,6 +57,7 @@
     private boolean mIsReadyForTransition;
     private Bundle mSharedElementsBundle;
     private boolean mWasOpaque;
+    private boolean mAreViewsReady;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -80,18 +82,11 @@
     }
 
     public void viewInstancesReady(ArrayList<String> accepted, ArrayList<View> localViews) {
-        if (mIsReadyForTransition) {
-            return;
-        }
-        viewsReady(mapSharedElements(accepted, localViews));
+        triggerViewsReady(mapSharedElements(accepted, localViews));
     }
 
     public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
-        if (mIsReadyForTransition) {
-            return;
-        }
-
-        viewsReady(mapNamedElements(accepted, localNames));
+        triggerViewsReady(mapNamedElements(accepted, localNames));
     }
 
     @Override
@@ -117,6 +112,27 @@
         }
     }
 
+    private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
+        if (mAreViewsReady) {
+            return;
+        }
+        mAreViewsReady = true;
+        // Ensure the views have been laid out before capturing the views -- we need the epicenter.
+        if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) {
+            viewsReady(sharedElements);
+        } else {
+            sharedElements.valueAt(0).getViewTreeObserver()
+                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    sharedElements.valueAt(0).getViewTreeObserver().removeOnPreDrawListener(this);
+                    viewsReady(sharedElements);
+                    return true;
+                }
+            });
+        }
+    }
+
     private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
             ArrayList<String> localNames) {
         ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
@@ -280,9 +296,21 @@
         setOriginalImageViewState(originalImageViewState);
 
         if (mResultReceiver != null) {
-            mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
+            // We can't trust that the view will disappear on the same frame that the shared
+            // element appears here. Assure that we get at least 2 frames for double-buffering.
+            getDecor().postOnAnimation(new Runnable() {
+                int mAnimations;
+                @Override
+                public void run() {
+                    if (mAnimations++ < MIN_ANIMATION_FRAMES) {
+                        getDecor().postOnAnimation(this);
+                    } else {
+                        mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
+                        mResultReceiver = null; // all done sending messages.
+                    }
+                }
+            });
         }
-        mResultReceiver = null; // all done sending messages.
     }
 
     @Override
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 49f2a5d..93cba0d 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -29,6 +29,7 @@
 import android.transition.TransitionManager;
 import android.util.ArrayMap;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroupOverlay;
 import android.view.ViewTreeObserver;
 
@@ -65,8 +66,6 @@
 
     private Bundle mExitSharedElementBundle;
 
-    private ArrayList<View> mSharedElementSnapshots;
-
     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
         super(activity.getWindow(), names, getListener(activity, isReturning),
@@ -124,6 +123,8 @@
                     startSharedElementExit();
                 }
             });
+        } else {
+            sharedElementTransitionComplete();
         }
     }
 
@@ -131,17 +132,14 @@
         Transition transition = getSharedElementExitTransition();
         final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
                 mSharedElementNames);
-        mSharedElementSnapshots = createSnapshots(mExitSharedElementBundle, mSharedElementNames);
         transition.addListener(new Transition.TransitionListenerAdapter() {
             @Override
             public void onTransitionEnd(Transition transition) {
                 transition.removeListener(this);
-                setViewVisibility(mSharedElements, View.INVISIBLE);
-                ViewGroupOverlay overlay = getDecor().getOverlay();
-                if (mSharedElementSnapshots != null) {
-                    for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
-                        overlay.add(mSharedElementSnapshots.get(i));
-                    }
+                int count = mSharedElements.size();
+                for (int i = 0; i < count; i++) {
+                    View sharedElement = mSharedElements.get(i);
+                    ((ViewGroup)sharedElement.getParent()).suppressLayout(true);
                 }
             }
         });
@@ -158,28 +156,10 @@
         getDecor().invalidate();
     }
 
-    private static ArrayList<View> copySnapshots(ArrayList<View> snapshots) {
-        ArrayList<View> copy = new ArrayList<View>(snapshots.size());
-        for (int i = 0; i < snapshots.size(); i++) {
-            View view = snapshots.get(i);
-            View viewCopy = new View(view.getContext());
-            viewCopy.setBackground(view.getBackground());
-            copy.add(viewCopy);
-        }
-        return copy;
-    }
-
     private void hideSharedElements() {
         if (!mIsHidden) {
             setViewVisibility(mSharedElements, View.INVISIBLE);
         }
-        if (mSharedElementSnapshots != null) {
-            ViewGroupOverlay overlay = getDecor().getOverlay();
-            for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
-                overlay.remove(mSharedElementSnapshots.get(i));
-            }
-            mSharedElementSnapshots = null;
-        }
         finishIfNecessary();
     }
 
@@ -360,7 +340,8 @@
     }
 
     private void finishIfNecessary() {
-        if (mIsReturning && mExitNotified && mActivity != null && mSharedElementSnapshots == null) {
+        if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() ||
+                mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
             finish();
         }
         if (!mIsReturning && mExitNotified) {
@@ -382,7 +363,6 @@
             mBackgroundAnimator = null;
         }
         mExitSharedElementBundle = null;
-        mSharedElementSnapshots = null;
         clearState();
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d18647a..df51ff5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2586,6 +2586,8 @@
      * @param packages The list of packages allowed to enter lock task mode
      *
      * @see Activity#startLockTask()
+     * @see DeviceAdminReceiver#onLockTaskModeChanged(Context, Intent, boolean, String)
+     * @see UserManager#DISALLOW_CREATE_WINDOWS
      */
     public void setLockTaskPackages(String[] packages) throws SecurityException {
         if (mService != null) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 64d80a0..a94bc41 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -306,6 +306,11 @@
     public static final String ACTION_UUID =
             "android.bluetooth.device.action.UUID";
 
+    /** @hide */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MAS_INSTANCE =
+            "android.bluetooth.device.action.MAS_INSTANCE";
+
     /**
      * Broadcast Action: Indicates a failure to retrieve the name of a remote
      * device.
@@ -522,14 +527,17 @@
      * Prefer BR/EDR transport for GATT connections to remote dual-mode devices
      * @hide
      */
-   public static final int TRANSPORT_BREDR = 1;
+    public static final int TRANSPORT_BREDR = 1;
 
     /**
      * Prefer LE transport for GATT connections to remote dual-mode devices
      * @hide
      */
-   public static final int TRANSPORT_LE = 2;
+    public static final int TRANSPORT_LE = 2;
 
+    /** @hide */
+    public static final String EXTRA_MAS_INSTANCE =
+        "android.bluetooth.device.extra.MAS_INSTANCE";
 
     /**
      * Lazy initialization. Guaranteed final after first object constructed, or
@@ -995,6 +1003,18 @@
             return false;
     }
 
+     /** @hide */
+     public boolean fetchMasInstances() {
+         if (sService == null) {
+             Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances");
+             return false;
+         }
+         try {
+             return sService.fetchRemoteMasInstances(this);
+         } catch (RemoteException e) {Log.e(TAG, "", e);}
+         return false;
+     }
+
     /** @hide */
     public int getServiceChannel(ParcelUuid uuid) {
         //TODO(BT)
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 2e993c9d..fbbc2f7 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -32,12 +32,12 @@
  * Public API for the Bluetooth GATT Profile server role.
  *
  * <p>This class provides Bluetooth GATT server role functionality,
- * allowing applications to create and advertise Bluetooth Smart services
- * and characteristics.
+ * allowing applications to create Bluetooth Smart services and
+ * characteristics.
  *
  * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
- * via IPC.  Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothGatt proxy object.
+ * via IPC.  Use {@link BluetoothManager#openGattServer} to get an instance
+ * of this class.
  */
 public final class BluetoothGattServer implements BluetoothProfile {
     private static final String TAG = "BluetoothGattServer";
@@ -545,7 +545,7 @@
     /**
      * Add a service to the list of services to be hosted.
      *
-     * <p>Once a service has been addded to the the list, the service and it's
+     * <p>Once a service has been addded to the the list, the service and its
      * included characteristics will be provided by the local device.
      *
      * <p>If the local device has already exposed services when this function
diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java
new file mode 100644
index 0000000..4459e2c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothMasInstance.java
@@ -0,0 +1,103 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class BluetoothMasInstance implements Parcelable {
+    private final int mId;
+    private final String mName;
+    private final int mChannel;
+    private final int mMsgTypes;
+
+    public BluetoothMasInstance(int id, String name, int channel, int msgTypes) {
+        mId = id;
+        mName = name;
+        mChannel = channel;
+        mMsgTypes = msgTypes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothMasInstance) {
+            return mId == ((BluetoothMasInstance)o).mId;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mId + (mChannel << 8) + (mMsgTypes << 16);
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" +
+                Integer.toHexString(mMsgTypes);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothMasInstance> CREATOR =
+            new Parcelable.Creator<BluetoothMasInstance>() {
+        public BluetoothMasInstance createFromParcel(Parcel in) {
+            return new BluetoothMasInstance(in.readInt(), in.readString(),
+                    in.readInt(), in.readInt());
+        }
+        public BluetoothMasInstance[] newArray(int size) {
+            return new BluetoothMasInstance[size];
+        }
+    };
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mId);
+        out.writeString(mName);
+        out.writeInt(mChannel);
+        out.writeInt(mMsgTypes);
+    }
+
+    public static final class MessageType {
+        public static final int EMAIL    = 0x01;
+        public static final int SMS_GSM  = 0x02;
+        public static final int SMS_CDMA = 0x04;
+        public static final int MMS      = 0x08;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public int getChannel() {
+        return mChannel;
+    }
+
+    public int getMsgTypes() {
+        return mMsgTypes;
+    }
+
+    public boolean msgSupported(int msg) {
+        return (mMsgTypes & msg) != 0;
+    }
+}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index a45c6b8..df6037e 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -67,6 +67,7 @@
     int getRemoteClass(in BluetoothDevice device);
     ParcelUuid[] getRemoteUuids(in BluetoothDevice device);
     boolean fetchRemoteUuids(in BluetoothDevice device);
+    boolean fetchRemoteMasInstances(in BluetoothDevice device);
 
     boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode);
     boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[]
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 58f1c84..9f79a89 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -21,6 +21,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Printer;
 
@@ -86,8 +87,15 @@
      * {@link PackageManager#GET_META_DATA} flag when requesting the info.
      */
     public Bundle metaData;
-    
+
+    /**
+     * If different of UserHandle.USER_NULL, The icon of this item will be the one of that user.
+     * @hide
+     */
+    public int showUserIcon;
+
     public PackageItemInfo() {
+        showUserIcon = UserHandle.USER_NULL;
     }
 
     public PackageItemInfo(PackageItemInfo orig) {
@@ -101,6 +109,7 @@
         banner = orig.banner;
         logo = orig.logo;
         metaData = orig.metaData;
+        showUserIcon = orig.showUserIcon;
     }
 
     /**
@@ -143,8 +152,8 @@
      * such as the default activity icon.
      */
     public Drawable loadIcon(PackageManager pm) {
-        if (icon != 0) {
-            Drawable dr = pm.getDrawable(packageName, icon, getApplicationInfo());
+        if (icon != 0 || showUserIcon != UserHandle.USER_NULL) {
+            Drawable dr = pm.loadItemIcon(this, getApplicationInfo());
             if (dr != null) {
                 return dr;
             }
@@ -288,6 +297,7 @@
         dest.writeInt(logo);
         dest.writeBundle(metaData);
         dest.writeInt(banner);
+        dest.writeInt(showUserIcon);
     }
     
     protected PackageItemInfo(Parcel source) {
@@ -300,6 +310,7 @@
         logo = source.readInt();
         metaData = source.readBundle();
         banner = source.readInt();
+        showUserIcon = source.readInt();
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ac433f..d0b802c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3642,5 +3642,5 @@
     /**
      * @hide
      */
-    public abstract Bitmap getUserIcon(int userId);
+    public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo);
 }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 1f9d60c..fe3aec9 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,8 +19,6 @@
 import android.content.ComponentName;
 import android.content.IntentFilter;
 import android.graphics.drawable.Drawable;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -136,10 +134,9 @@
     public int targetUserId;
 
     /**
-     * If true, then loadIcon will return the icon of the target user.
      * @hide
      */
-    public boolean showTargetUserIcon;
+    public boolean noResourceId;
 
     /**
      * @hide Target comes from system process?
@@ -217,10 +214,6 @@
                 return dr;
             }
         }
-        if (showTargetUserIcon) {
-            Bitmap bm = pm.getUserIcon(targetUserId);
-            return new BitmapDrawable(bm);
-        }
         return ci.loadIcon(pm);
     }
     
@@ -232,9 +225,10 @@
      * @return The icon associated with this match.
      */
     public final int getIconResource() {
+        if (noResourceId) return 0;
         if (icon != 0) return icon;
         final ComponentInfo ci = getComponentInfo();
-        if (ci != null && !showTargetUserIcon) {
+        if (ci != null) {
             return ci.getIconResource();
         }
         return 0;
@@ -312,9 +306,6 @@
             sb.append(" targetUserId=");
             sb.append(targetUserId);
         }
-        if (showTargetUserIcon) {
-            sb.append(" [showTargetUserIcon]");
-        }
         sb.append('}');
         return sb.toString();
     }
@@ -351,8 +342,8 @@
         dest.writeInt(icon);
         dest.writeString(resolvePackageName);
         dest.writeInt(targetUserId);
-        dest.writeInt(showTargetUserIcon ? 1 : 0);
         dest.writeInt(system ? 1 : 0);
+        dest.writeInt(noResourceId ? 1 : 0);
     }
 
     public static final Creator<ResolveInfo> CREATOR
@@ -396,8 +387,8 @@
         icon = source.readInt();
         resolvePackageName = source.readString();
         targetUserId = source.readInt();
-        showTargetUserIcon = source.readInt() != 0;
         system = source.readInt() != 0;
+        noResourceId = source.readInt() != 0;
     }
     
     public static class DisplayNameComparator
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 14e6f92..db08d6c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1797,6 +1797,8 @@
      * </ul>
      * <p>A LIMITED device may have some or none of the above characteristics.
      * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+     * <p>A LEGACY device does not support per-frame control, manual sensor control, manual
+     * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC
@@ -1806,6 +1808,7 @@
      * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+     * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
      */
     public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
             new Key<Integer>("android.info.supportedHardwareLevel", int.class);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e464f2a..53c8a52 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -577,12 +577,17 @@
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0;
 
     /**
-     * <p>This camera device is capable of supporting advanced imaging
-     * applications.</p>
+     * <p>This camera device is capable of supporting advanced imaging applications.</p>
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1;
 
+    /**
+     * <p>This camera device is running in backward compatibility mode.</p>
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     */
+    public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2;
+
     //
     // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
     //
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 4b39092..79f4403 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -33,6 +33,7 @@
 import android.view.Surface;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
@@ -271,6 +272,9 @@
             return BAD_VALUE;
         }
 
+        List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
+                getSurfaceIds(mConfiguredSurfaces);
+
         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
         for (CaptureRequest request : requestList) {
             if (request.getTargets().isEmpty()) {
@@ -287,7 +291,7 @@
                     Log.e(TAG, "submitRequestList - must configure " +
                             " device with valid surfaces before submitting requests");
                     return INVALID_OPERATION;
-                } else if (!mConfiguredSurfaces.contains(surface)) {
+                } else if (!containsSurfaceId(surface, surfaceIds)) {
                     Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
                     return BAD_VALUE;
                 }
@@ -430,6 +434,32 @@
         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
     }
 
+    static long getSurfaceId(Surface surface) {
+        checkNotNull(surface);
+        return nativeGetSurfaceId(surface);
+    }
+
+    static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
+        if (surfaces == null) {
+            throw new NullPointerException("Null argument surfaces");
+        }
+        List<Long> surfaceIds = new ArrayList<>();
+        for (Surface s : surfaces) {
+            long id = getSurfaceId(s);
+            if (id == 0) {
+                throw new IllegalStateException(
+                        "Configured surface had null native GraphicBufferProducer pointer!");
+            }
+            surfaceIds.add(id);
+        }
+        return surfaceIds;
+    }
+
+    static boolean containsSurfaceId(Surface s, List<Long> ids) {
+        long id = getSurfaceId(s);
+        return ids.contains(id);
+    }
+
     private static native int nativeDetectSurfaceType(Surface surface);
 
     private static native int nativeDetectSurfaceDimens(Surface surface,
@@ -445,4 +475,5 @@
 
     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
 
+    private static native long nativeGetSurfaceId(Surface surface);
 }
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index e38624a..9969fd2 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -492,19 +492,20 @@
                 && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
             return;
         }
+
         checkGlError("before updateTexImage");
         mSurfaceTexture.updateTexImage();
         if (targetSurfaces == null) return;
+        List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
         for (EGLSurfaceHolder holder : mSurfaces) {
-            if (targetSurfaces.contains(holder.surface)) {
+            if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
                 makeCurrent(holder.eglSurface);
                 drawFrame(mSurfaceTexture);
                 swapBuffers(holder.eglSurface);
             }
-
         }
         for (EGLSurfaceHolder holder : mConversionSurfaces) {
-            if (targetSurfaces.contains(holder.surface)) {
+            if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
                 makeCurrent(holder.eglSurface);
                 drawFrame(mSurfaceTexture);
                 mPBufferPixels.clear();
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index c87b674..d71a411 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -104,7 +104,6 @@
     /** Logical address used to indicate the source comes from internal device. */
     public static final int ADDR_INTERNAL = 0xFFFF;
 
-    // TODO: Complete the list of CEC messages definition.
     public static final int MESSAGE_FEATURE_ABORT = 0x00;
     public static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
     public static final int MESSAGE_TUNER_STEP_INCREMENT = 0x05;
@@ -190,6 +189,55 @@
     public static final int RESULT_EXCEPTION = 5;
     public static final int RESULT_INCORRECT_MODE = 6;
 
+    // Definitions used for setOption(). These should be in sync with the definition
+    // in hardware/libhardware/include/hardware/{hdmi_cec.h,mhl.h}.
+
+    /**
+     * TV gets turned on by incoming &lt;Text/Image View On&gt;. {@code ENABLED} by default.
+     * If set to {@code DISABLED}, TV won't turn on automatically.
+     */
+    public static final int OPTION_CEC_AUTO_WAKEUP = 1;
+
+    /**
+     * If set to {@code DISABLED}, all CEC commands are discarded.
+     *
+     * <p> This option is for internal use only, not supposed to be used by other components.
+     * @hide
+     */
+    public static final int OPTION_CEC_ENABLE = 2;
+
+    /**
+     * If set to {@code DISABLED}, system service yields control of CEC to sub-microcontroller.
+     * If {@code ENABLED}, it take the control back.
+     *
+     * <p> This option is for internal use only, not supposed to be used by other components.
+     * @hide
+     */
+    public static final int OPTION_CEC_SERVICE_CONTROL = 3;
+
+    /**
+     * Put other devices to standby when TV goes to standby. {@code ENABLED} by default.
+     * If set to {@code DISABLED}, TV doesn't send &lt;Standby&gt; to other devices.
+     */
+    public static final int OPTION_CEC_AUTO_DEVICE_OFF = 4;
+
+    /** If set to {@code DISABLED}, TV does not switch ports when mobile device is connected. */
+    public static final int OPTION_MHL_INPUT_SWITCHING = 101;
+
+    /** If set to {@code ENABLED}, TV disables power charging for mobile device. */
+    public static final int OPTION_MHL_POWER_CHARGE = 102;
+
+    /**
+     * If set to {@code DISABLED}, all MHL commands are discarded.
+     *
+     * <p> This option is for internal use only, not supposed to be used by other components.
+     * @hide
+     */
+    public static final int OPTION_MHL_ENABLE = 103;
+
+    public static final int DISABLED = 0;
+    public static final int ENABLED = 1;
+
     private static final int[] ADDRESS_TO_TYPE = {
         DEVICE_TV,  // ADDR_TV
         DEVICE_RECORDER,  // ADDR_RECORDER_1
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index fbf75d3..f2948e7 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -50,4 +50,5 @@
     void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
     void setControlEnabled(boolean enabled);
     void setArcMode(boolean enabled);
+    void setOption(int option, int value);
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a8b324a..05e179d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2208,15 +2208,15 @@
         if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
         try {
             incCallbackHandlerRefCount();
-            if (action == LISTEN) {
-                networkCallback.networkRequest = mService.listenForNetwork(need,
-                        new Messenger(sCallbackHandler), new Binder());
-            } else {
-                networkCallback.networkRequest = mService.requestNetwork(need,
-                        new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
-            }
-            if (networkCallback.networkRequest != null) {
-                synchronized(sNetworkCallback) {
+            synchronized(sNetworkCallback) {
+                if (action == LISTEN) {
+                    networkCallback.networkRequest = mService.listenForNetwork(need,
+                            new Messenger(sCallbackHandler), new Binder());
+                } else {
+                    networkCallback.networkRequest = mService.requestNetwork(need,
+                            new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
+                }
+                if (networkCallback.networkRequest != null) {
                     sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
                 }
             }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index df6057e..1129c9e 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -31,11 +31,15 @@
 
     /** Control UID policies. */
     void setUidPolicy(int uid, int policy);
+    void addUidPolicy(int uid, int policy);
+    void removeUidPolicy(int uid, int policy);
     int getUidPolicy(int uid);
     int[] getUidsWithPolicy(int policy);
 
     boolean isUidForeground(int uid);
 
+    int[] getPowerSaveAppIdWhitelist();
+
     void registerListener(INetworkPolicyListener listener);
     void unregisterListener(INetworkPolicyListener listener);
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2cd1f9b..a8e7757 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -45,6 +45,8 @@
     public static final int POLICY_NONE = 0x0;
     /** Reject network usage on metered networks when application in background. */
     public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
+    /** Allow network use (metered or not) in the background in battery save mode. */
+    public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
 
     /** All network traffic should be allowed. */
     public static final int RULE_ALLOW_ALL = 0x0;
@@ -76,7 +78,7 @@
      * Set policy flags for specific UID.
      *
      * @param policy {@link #POLICY_NONE} or combination of flags like
-     *            {@link #POLICY_REJECT_METERED_BACKGROUND}.
+     * {@link #POLICY_REJECT_METERED_BACKGROUND}, {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
      */
     public void setUidPolicy(int uid, int policy) {
         try {
@@ -85,6 +87,30 @@
         }
     }
 
+    /**
+     * Add policy flags for specific UID.  The given policy bits will be set for
+     * the uid.  Policy flags may be either
+     * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+     */
+    public void addUidPolicy(int uid, int policy) {
+        try {
+            mService.addUidPolicy(uid, policy);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Clear/remove policy flags for specific UID.  The given policy bits will be set for
+     * the uid.  Policy flags may be either
+     * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+     */
+    public void removeUidPolicy(int uid, int policy) {
+        try {
+            mService.removeUidPolicy(uid, policy);
+        } catch (RemoteException e) {
+        }
+    }
+
     public int getUidPolicy(int uid) {
         try {
             return mService.getUidPolicy(uid);
@@ -101,6 +127,14 @@
         }
     }
 
+    public int[] getPowerSaveAppIdWhitelist() {
+        try {
+            return mService.getPowerSaveAppIdWhitelist();
+        } catch (RemoteException e) {
+            return new int[0];
+        }
+    }
+
     public void registerListener(INetworkPolicyListener listener) {
         try {
             mService.registerListener(listener);
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index c09492c..c599dfc 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -81,6 +81,13 @@
     public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS";
 
     /**
+     * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an
+     * array of {@link NetworkKey}s. Can be obtained with
+     * {@link android.content.Intent#getParcelableArrayExtra(String)}}.
+     */
+    public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
+
+    /**
      * Activity action: launch a custom activity for configuring a scorer before enabling it.
      * Scorer applications may choose to specify an activity for this action, in which case the
      * framework will launch that activity which should return RESULT_OK if scoring was enabled.
@@ -92,11 +99,24 @@
     public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
 
     /**
-     * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an
-     * array of {@link NetworkKey}s. Can be obtained with
-     * {@link android.content.Intent#getParcelableArrayExtra(String)}}.
+     * Broadcast action: the active scorer has been changed. Scorer apps may listen to this to
+     * perform initialization once selected as the active scorer, or clean up unneeded resources
+     * if another scorer has been selected. Note that it is unnecessary to clear existing scores as
+     * this is handled by the system.
+     *
+     * <p>The new scorer will be specified in {@link #EXTRA_NEW_SCORER}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
      */
-    public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SCORER_CHANGED = "android.net.scoring.SCORER_CHANGED";
+
+    /**
+     * Extra used with {@link #ACTION_SCORER_CHANGED} to specify the newly selected scorer's package
+     * name. Will be null if scoring was disabled. Can be obtained with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_NEW_SCORER = "newScorer";
 
     private final Context mContext;
     private final INetworkScoreService mService;
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 53e87a6..541b700 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.content.IntentFilter;
+import android.nfc.BeamShareData;
 import android.nfc.NdefMessage;
 import android.nfc.Tag;
 import android.nfc.TechListParcel;
@@ -47,7 +48,8 @@
     void setForegroundDispatch(in PendingIntent intent,
             in IntentFilter[] filters, in TechListParcel techLists);
     void setAppCallback(in IAppCallback callback);
-    void invokeBeam();
+    oneway void invokeBeam();
+    oneway void invokeBeamInternal(in BeamShareData shareData);
 
     void dispatch(in Tag tag);
 
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index be098a8..b0397d5 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -29,6 +29,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.nfc.BeamShareData;
 import android.nfc.tech.MifareClassic;
 import android.nfc.tech.Ndef;
 import android.nfc.tech.NfcA;
@@ -1274,6 +1275,21 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean invokeBeam(BeamShareData shareData) {
+        try {
+            Log.e(TAG, "invokeBeamInternal()");
+            sService.invokeBeamInternal(shareData);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "invokeBeam: NFC process has died.");
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
      * Enable NDEF message push over NFC while this Activity is in the foreground.
      *
      * <p>You must explicitly call this method every time the activity is
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index cabda5d..34568c2 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -16,7 +16,7 @@
  * The AidGroup class represents a group of Application Identifiers (AIDs).
  *
  * <p>An instance of this object can be used with
- * {@link CardEmulation#registerAidGroupForService(android.content.ComponentName, AidGroup)}
+ * {@link CardEmulation#registerAidsForService(android.content.ComponentName, String, java.util.List)}
  * to tell the OS which AIDs are handled by your HCE- or SE-based service.
  *
  * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
@@ -50,6 +50,11 @@
         if (aids.size() > MAX_NUM_AIDS) {
             throw new IllegalArgumentException("Too many AIDs in AID group.");
         }
+        for (String aid : aids) {
+            if (!ApduServiceInfo.isValidAid(aid)) {
+                throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
+            }
+        }
         if (isValidCategory(category)) {
             this.category = category;
         } else {
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index f379ee8..930cf11 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -351,20 +351,37 @@
         }
     }
 
+    /**
+     * A valid AID according to ISO/IEC 7816-4:
+     * <ul>
+     * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
+     * <li>Consist of only hex characters
+     * <li>Additionally, we allow an asterisk at the end, to indicate
+     *     a prefix
+     * </ul>
+     */
     static boolean isValidAid(String aid) {
         if (aid == null)
             return false;
 
-        int aidLength = aid.length();
-        if (aidLength == 0 || (aidLength % 2) != 0) {
-            Log.e(TAG, "AID " + aid + " is not correctly formatted.");
+        // If a prefix AID, the total length must be odd (even # of AID chars + '*')
+        if (aid.endsWith("*") && ((aid.length() % 2) == 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
             return false;
         }
-        // Minimum AID length is 5 bytes, 10 hex chars
-        if (aidLength < 10) {
-            Log.e(TAG, "AID " + aid + " is shorter than 5 bytes.");
+
+        // If not a prefix AID, the total length must be even (even # of AID chars)
+        if (!aid.endsWith("*") && ((aid.length() % 2) != 0)) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
             return false;
         }
+
+        // Verify hex characters
+        if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?")) {
+            Log.e(TAG, "AID " + aid + " is not a valid AID.");
+            return false;
+        }
+
         return true;
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f7d2bfd..21d60c5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -614,6 +614,7 @@
         public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
         public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29;
         public static final int STATE2_WIFI_ON_FLAG = 1<<28;
+        public static final int STATE2_FLASHLIGHT_FLAG = 1<<27;
 
         public static final int MOST_INTERESTING_STATES2 =
             STATE2_LOW_POWER_FLAG | STATE2_WIFI_ON_FLAG;
@@ -1235,6 +1236,7 @@
         new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
         new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"),
+        new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"),
         new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
                 HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss",
                 new String[] { "0", "1", "2", "3", "4" },
@@ -1370,6 +1372,22 @@
      */
     public abstract int getBluetoothStateCount(int bluetoothState, int which);
 
+    /**
+     * Returns the time in microseconds that the flashlight has been on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getFlashlightOnTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the flashlight has been turned on while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getFlashlightOnCount(int which);
+
     public static final int NETWORK_MOBILE_RX_DATA = 0;
     public static final int NETWORK_MOBILE_TX_DATA = 1;
     public static final int NETWORK_WIFI_RX_DATA = 2;
@@ -1965,6 +1983,9 @@
                     case SCREEN:
                         label="scrn";
                         break;
+                    case FLASHLIGHT:
+                        label="flashlight";
+                        break;
                     case APP:
                         uid = bs.uidObj.getUid();
                         label = "uid";
@@ -2637,6 +2658,10 @@
                         pw.print(prefix); pw.print("    Screen: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
+                    case FLASHLIGHT:
+                        pw.print(prefix); pw.print("    Flashlight: "); printmAh(pw, bs.value);
+                        pw.println();
+                        break;
                     case APP:
                         pw.print(prefix); pw.print("    Uid ");
                         UserHandle.formatUid(pw, bs.uidObj.getUid());
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b980e81..a54320f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.Drawable;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
 
@@ -278,6 +279,24 @@
      */
     public static final String DISALLOW_TELEPHONY = "no_telephony";
 
+    /**
+     * Key for user restrictions. Specifies that windows besides app windows should not be
+     * created. This will block the creation of the following types of windows.
+     * <li>{@link LayoutParams#TYPE_TOAST}</li>
+     * <li>{@link LayoutParams#TYPE_PHONE}</li>
+     * <li>{@link LayoutParams#TYPE_PRIORITY_PHONE}</li>
+     * <li>{@link LayoutParams#TYPE_SYSTEM_ALERT}</li>
+     * <li>{@link LayoutParams#TYPE_SYSTEM_ERROR}</li>
+     * <li>{@link LayoutParams#TYPE_SYSTEM_OVERLAY}</li>
+     *
+     * <p>The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+
     /** @hide */
     public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
     /** @hide */
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b839613..2f061f1 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -36,6 +36,7 @@
 import android.database.DatabaseUtils;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.net.Uri.Builder;
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
@@ -171,13 +172,16 @@
      * A key to a boolean in the "extras" bundle of the cursor.
      * The boolean indicates that the provider did not create a snippet and that the client asking
      * for the snippet should do it (true means the snippeting was deferred to the client).
+     *
+     * @see SearchSnippets
      */
     public static final String DEFERRED_SNIPPETING = "deferred_snippeting";
 
     /**
-     * Key to retrieve the original query on the client side.
+     * Key to retrieve the original deferred snippeting from the cursor on the client side.
      *
-     * @hide
+     * @see SearchSnippets
+     * @see #DEFERRED_SNIPPETING
      */
     public static final String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
 
@@ -5089,42 +5093,75 @@
     /**
      * Additional column returned by
      * {@link ContactsContract.Contacts#CONTENT_FILTER_URI Contacts.CONTENT_FILTER_URI} explaining
-     * why the filter matched the contact.  Specifically, it contains the data elements that
-     * matched the query.  The overall number of words in the snippet can be capped.
+     * why the filter matched the contact. This column will contain extracts from the contact's
+     * constituent {@link Data Data} items, formatted in a way that indicates the section of the
+     * snippet that matched the filter.
+     *
+     * <p>
+     * The following example searches for all contacts that match the query "presi" and requests
+     * the snippet column as well.
+     * <pre>
+     * Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
+     * builder.appendPath("presi");
+     * // Defer snippeting to the client side if possible, for performance reasons.
+     * builder.appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY,"1");
+     *
+     * Cursor cursor = getContentResolver().query(builder.build());
+     *
+     * Bundle extras = cursor.getExtras();
+     * if (extras.getBoolean(ContactsContract.DEFERRED_SNIPPETING)) {
+     *     // Do our own snippet formatting.
+     *     // For a contact with the email address (president@organization.com), the snippet
+     *     // column will contain the string "president@organization.com".
+     * } else {
+     *     // The snippet has already been pre-formatted, we can display it as is.
+     *     // For a contact with the email address (president@organization.com), the snippet
+     *     // column will contain the string "[presi]dent@organization.com".
+     * }
+     * </pre>
+     * </p>
      */
-    public static class SearchSnippetColumns {
+    public static class SearchSnippets {
 
         /**
-         * The search snippet constructed according to the SQLite rules, see
-         * http://www.sqlite.org/fts3.html#snippet
+         * The search snippet constructed by SQLite snippeting functionality.
          * <p>
-         * The snippet may contain (parts of) several data elements comprising
-         * the contact.
+         * The snippet may contain (parts of) several data elements belonging to the contact,
+         * with the matching parts optionally surrounded by special characters that indicate the
+         * start and end of matching text.
+         *
+         * For example, if a contact has an address "123 Main Street", using a filter "mai" would
+         * return the formatted snippet "123 [Mai]n street".
+         *
+         * @see <a href="http://www.sqlite.org/fts3.html#snippet">
+         *         http://www.sqlite.org/fts3.html#snippet</a>
          */
         public static final String SNIPPET = "snippet";
 
-
         /**
          * Comma-separated parameters for the generation of the snippet:
          * <ul>
-         * <li>The "start match" text. Default is &lt;b&gt;</li>
-         * <li>The "end match" text. Default is &lt;/b&gt;</li>
-         * <li>The "ellipsis" text. Default is &lt;b&gt;...&lt;/b&gt;</li>
+         * <li>The "start match" text. Default is '['</li>
+         * <li>The "end match" text. Default is ']'</li>
+         * <li>The "ellipsis" text. Default is "..."</li>
          * <li>Maximum number of tokens to include in the snippet. Can be either
          * a positive or a negative number: A positive number indicates how many
          * tokens can be returned in total. A negative number indicates how many
          * tokens can be returned per occurrence of the search terms.</li>
          * </ul>
+         *
+         * @hide
          */
         public static final String SNIPPET_ARGS_PARAM_KEY = "snippet_args";
 
         /**
-         * A key to ask the provider to defer the snippeting to the client if possible.
-         * Value of 1 implies true, 0 implies false when 0 is the default.
+         * The key to ask the provider to defer the formatting of the snippet to the client if
+         * possible, for performance reasons.
+         * A value of 1 indicates true, 0 indicates false. False is the default.
          * When a cursor is returned to the client, it should check for an extra with the name
          * {@link ContactsContract#DEFERRED_SNIPPETING} in the cursor. If it exists, the client
-         * should do its own snippeting. If it doesn't exist, the snippet column in the cursor
-         * should already contain a snippetized string.
+         * should do its own formatting of the snippet. If it doesn't exist, the snippet column
+         * in the cursor should already contain a formatted snippet.
          */
         public static final String DEFERRED_SNIPPETING_KEY = "deferred_snippeting";
     }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 03ce4e0..f3c26c8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -154,6 +154,7 @@
         final Rect mWinFrame = new Rect();
         final Rect mOverscanInsets = new Rect();
         final Rect mContentInsets = new Rect();
+        final Rect mStableInsets = new Rect();
         final Configuration mConfiguration = new Configuration();
         
         final WindowManager.LayoutParams mLayout
@@ -253,7 +254,8 @@
         final BaseIWindow mWindow = new BaseIWindow() {
             @Override
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-                    Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+                    Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+                    Configuration newConfig) {
                 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0);
                 mCaller.sendMessage(msg);
@@ -628,7 +630,7 @@
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
-                            mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
+                            mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
index 03e53ec..c13b47f 100644
--- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
@@ -16,9 +16,12 @@
 package android.speech.tts;
 
 import android.content.Context;
+import android.media.AudioSystem;
+import android.media.AudioTrack;
 import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.ConditionVariable;
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 import android.util.Log;
 
@@ -27,7 +30,7 @@
 
     private final Context mContext;
     private final Uri mUri;
-    private final int mStreamType;
+    private final AudioOutputParams mAudioParams;
 
     private final ConditionVariable mDone;
     private MediaPlayer mPlayer;
@@ -35,12 +38,12 @@
 
     AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher,
             Object callerIdentity,
-            Context context, Uri uri, int streamType) {
+            Context context, Uri uri, AudioOutputParams audioParams) {
         super(dispatcher, callerIdentity);
 
         mContext = context;
         mUri = uri;
-        mStreamType = streamType;
+        mAudioParams = audioParams;
 
         mDone = new ConditionVariable();
         mPlayer = null;
@@ -73,7 +76,11 @@
                     mDone.open();
                 }
             });
-            mPlayer.setAudioStreamType(mStreamType);
+            mPlayer.setAudioStreamType(mAudioParams.mStreamType);
+            setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
+            if (mAudioParams.mSessionId != AudioSystem.AUDIO_SESSION_ALLOCATE) {
+                mPlayer.setAudioSessionId(mAudioParams.mSessionId);
+            }
             mPlayer.start();
             mDone.block();
             finish();
@@ -89,6 +96,23 @@
         }
     }
 
+    private static void setupVolume(MediaPlayer player, float volume, float pan) {
+        final float vol = clip(volume, 0.0f, 1.0f);
+        final float panning = clip(pan, -1.0f, 1.0f);
+
+        float volLeft = vol, volRight = vol;
+        if (panning > 0.0f) {
+            volLeft *= (1.0f - panning);
+        } else if (panning < 0.0f) {
+            volRight *= (1.0f + panning);
+        }
+        player.setVolume(volLeft, volRight);
+    }
+
+    private static final float clip(float value, float min, float max) {
+        return value < min ? min : (value < max ? value : max);
+    }
+
     private void finish() {
         try {
             mPlayer.stop();
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index 92bb0ac..b405de0 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -4,6 +4,7 @@
 
 import android.media.AudioFormat;
 import android.media.AudioTrack;
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
 import android.util.Log;
 
 /**
@@ -44,12 +45,11 @@
     private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
 
 
-    private final int mStreamType;
+    private final AudioOutputParams mAudioParams;
     private final int mSampleRateInHz;
     private final int mAudioFormat;
     private final int mChannelCount;
-    private final float mVolume;
-    private final float mPan;
+
 
     private final int mBytesPerFrame;
     /**
@@ -73,15 +73,14 @@
     private AudioTrack mAudioTrack;
     private volatile boolean mStopped;
 
-    BlockingAudioTrack(int streamType, int sampleRate,
-            int audioFormat, int channelCount,
-            float volume, float pan) {
-        mStreamType = streamType;
+    private int mSessionId;
+
+    BlockingAudioTrack(AudioOutputParams audioParams, int sampleRate,
+            int audioFormat, int channelCount) {
+        mAudioParams = audioParams;
         mSampleRateInHz = sampleRate;
         mAudioFormat = audioFormat;
         mChannelCount = channelCount;
-        mVolume = volume;
-        mPan = pan;
 
         mBytesPerFrame = AudioFormat.getBytesPerSample(mAudioFormat) * mChannelCount;
         mIsShortUtterance = false;
@@ -215,8 +214,9 @@
                 = AudioTrack.getMinBufferSize(mSampleRateInHz, channelConfig, mAudioFormat);
         int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
 
-        AudioTrack audioTrack = new AudioTrack(mStreamType, mSampleRateInHz, channelConfig,
-                mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
+        AudioTrack audioTrack = new AudioTrack(mAudioParams.mStreamType, mSampleRateInHz,
+                channelConfig, mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
+                mAudioParams.mSessionId);
         if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
             Log.w(TAG, "Unable to create audio track.");
             audioTrack.release();
@@ -225,7 +225,7 @@
 
         mAudioBufferSize = bufferSizeInBytes;
 
-        setupVolume(audioTrack, mVolume, mPan);
+        setupVolume(audioTrack, mAudioParams.mVolume, mAudioParams.mPan);
         return audioTrack;
     }
 
@@ -328,19 +328,11 @@
     }
 
     private static final long clip(long value, long min, long max) {
-        if (value < min) {
-            return min;
-        }
-
-        if (value > max) {
-            return max;
-        }
-
-        return value;
+        return value < min ? min : (value < max ? value : max);
     }
 
-    private static float clip(float value, float min, float max) {
-        return value > max ? max : (value < min ? min : value);
+    private static final float clip(float value, float min, float max) {
+        return value < min ? min : (value < max ? value : max);
     }
 
 }
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
index 4d322df..694f25a 100644
--- a/core/java/android/speech/tts/ITextToSpeechService.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -36,8 +36,10 @@
      * @param text The text to synthesize.
      * @param queueMode Determines what to do to requests already in the queue.
      * @param param Request parameters.
+     * @param utteranceId Unique identifier of synthesized utterance.
      */
-    int speak(in IBinder callingInstance, in String text, in int queueMode, in Bundle params);
+    int speak(in IBinder callingInstance, in CharSequence text, in int queueMode, in Bundle params,
+            String utteranceId);
 
     /**
      * Tells the engine to synthesize some speech and write it to a file.
@@ -47,10 +49,11 @@
      * @param text The text to synthesize.
      * @param fileDescriptor The file descriptor to write the synthesized audio to. Has to be
               writable.
+     * @param utteranceId Unique identifier of synthesized utterance.
      * @param param Request parameters.
      */
-    int synthesizeToFileDescriptor(in IBinder callingInstance, in String text,
-        in ParcelFileDescriptor fileDescriptor, in Bundle params);
+    int synthesizeToFileDescriptor(in IBinder callingInstance, in CharSequence text,
+        in ParcelFileDescriptor fileDescriptor, in Bundle params, String utteranceId);
 
     /**
      * Plays an existing audio resource.
@@ -59,9 +62,11 @@
      *        TextToSpeech object.
      * @param audioUri URI for the audio resource (a file or android.resource URI)
      * @param queueMode Determines what to do to requests already in the queue.
+     * @param utteranceId Unique identifier of synthesized utterance.
      * @param param Request parameters.
      */
-    int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params);
+    int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params,
+            String utteranceId);
 
     /**
      * Plays silence.
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 7e68d27..f850f10 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -15,6 +15,7 @@
  */
 package android.speech.tts;
 
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 import android.util.Log;
 
@@ -28,23 +29,7 @@
 
     private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
 
-    /**
-     * Audio stream type. Must be one of the STREAM_ contants defined in
-     * {@link android.media.AudioManager}.
-     */
-    private final int mStreamType;
-
-    /**
-     * Volume, in the range [0.0f, 1.0f]. The default value is
-     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
-     */
-    private final float mVolume;
-
-    /**
-     * Left/right position of the audio, in the range [-1.0f, 1.0f].
-     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
-     */
-    private final float mPan;
+    private final AudioOutputParams mAudioParams;
 
     /**
      * Guards {@link #mAudioTrackHandler}, {@link #mItem} and {@link #mStopped}.
@@ -65,13 +50,11 @@
     private final Object mCallerIdentity;
     private final AbstractEventLogger mLogger;
 
-    PlaybackSynthesisCallback(int streamType, float volume, float pan,
-            AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher,
-            Object callerIdentity, AbstractEventLogger logger, boolean clientIsUsingV2) {
+    PlaybackSynthesisCallback(AudioOutputParams audioParams, AudioPlaybackHandler audioTrackHandler,
+            UtteranceProgressDispatcher dispatcher, Object callerIdentity,
+            AbstractEventLogger logger, boolean clientIsUsingV2) {
         super(clientIsUsingV2);
-        mStreamType = streamType;
-        mVolume = volume;
-        mPan = pan;
+        mAudioParams = audioParams;
         mAudioTrackHandler = audioTrackHandler;
         mDispatcher = dispatcher;
         mCallerIdentity = callerIdentity;
@@ -161,7 +144,7 @@
                 return TextToSpeech.ERROR;
             }
             SynthesisPlaybackQueueItem item = new SynthesisPlaybackQueueItem(
-                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+                    mAudioParams, sampleRateInHz, audioFormat, channelCount,
                     mDispatcher, mCallerIdentity, mLogger);
             mAudioTrackHandler.enqueue(item);
             mItem = item;
diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
index 104f486..7423933 100644
--- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -15,6 +15,7 @@
  */
 package android.speech.tts;
 
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 import android.util.Log;
 
@@ -62,9 +63,8 @@
     private final BlockingAudioTrack mAudioTrack;
     private final AbstractEventLogger mLogger;
 
-    SynthesisPlaybackQueueItem(int streamType, int sampleRate,
-            int audioFormat, int channelCount,
-            float volume, float pan, UtteranceProgressDispatcher dispatcher,
+    SynthesisPlaybackQueueItem(AudioOutputParams audioParams, int sampleRate,
+            int audioFormat, int channelCount, UtteranceProgressDispatcher dispatcher,
             Object callerIdentity, AbstractEventLogger logger) {
         super(dispatcher, callerIdentity);
 
@@ -74,8 +74,7 @@
         mDone = false;
         mStatusCode = TextToSpeech.SUCCESS;
 
-        mAudioTrack = new BlockingAudioTrack(streamType, sampleRate, audioFormat,
-                channelCount, volume, pan);
+        mAudioTrack = new BlockingAudioTrack(audioParams, sampleRate, audioFormat, channelCount);
         mLogger = logger;
     }
 
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index 12a026b..eaacc06 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -34,7 +34,7 @@
  * and {@link TextToSpeech#synthesizeToFile}.
  */
 public final class SynthesisRequest {
-    private final String mText;
+    private final CharSequence mText;
     private final Bundle mParams;
     private String mLanguage;
     private String mCountry;
@@ -49,10 +49,25 @@
         mParams = new Bundle(params);
     }
 
+    public SynthesisRequest(CharSequence text, Bundle params) {
+        mText = text;
+        // Makes a copy of params.
+        mParams = new Bundle(params);
+    }
+
+    /**
+     * Gets the text which should be synthesized.
+     * @deprecated As of API level 20, replaced by {@link #getCharSequenceText}.
+     */
+    @Deprecated
+    public String getText() {
+        return mText.toString();
+    }
+
     /**
      * Gets the text which should be synthesized.
      */
-    public String getText() {
+    public CharSequence getCharSequenceText() {
         return mText;
     }
 
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index a338c19..e1c1767b 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -564,6 +564,17 @@
          * @see TextToSpeech#getFeatures(java.util.Locale)
          */
         public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
+
+        /**
+         * Parameter key to specify an audio session identifier (obtained from
+         * {@link AudioManager#allocateAudioSessionId()}) that will be used by the request audio
+         * output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect}
+         * objects with the synthesis (or earcon) output.
+         *
+         * @see TextToSpeech#speak(String, int, HashMap)
+         * @see TextToSpeech#playEarcon(String, int, HashMap)
+         */
+        public static final String KEY_PARAM_SESSION_ID = "sessionId";
     }
 
     private final Context mContext;
@@ -582,7 +593,7 @@
     // too.
     private final boolean mUseFallback;
     private final Map<String, Uri> mEarcons;
-    private final Map<String, Uri> mUtterances;
+    private final Map<CharSequence, Uri> mUtterances;
     private final Bundle mParams = new Bundle();
     private final TtsEngines mEnginesHelper;
     private final String mPackageName;
@@ -633,7 +644,7 @@
         mUseFallback = useFallback;
 
         mEarcons = new HashMap<String, Uri>();
-        mUtterances = new HashMap<String, Uri>();
+        mUtterances = new HashMap<CharSequence, Uri>();
         mUtteranceProgressListener = null;
 
         mEnginesHelper = new TtsEngines(mContext);
@@ -814,6 +825,40 @@
     }
 
     /**
+     * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text
+     * and a sound resource in a package. After a call to this method, subsequent calls to
+     * {@link #speak(String, int, HashMap)} will play the specified sound resource
+     * if it is available, or synthesize the text it is missing.
+     *
+     * @param text
+     *            The string of text. Example: <code>"south_south_east"</code>
+     *
+     * @param packagename
+     *            Pass the packagename of the application that contains the
+     *            resource. If the resource is in your own application (this is
+     *            the most common case), then put the packagename of your
+     *            application here.<br/>
+     *            Example: <b>"com.google.marvin.compass"</b><br/>
+     *            The packagename can be found in the AndroidManifest.xml of
+     *            your application.
+     *            <p>
+     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
+     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
+     *            </p>
+     *
+     * @param resourceId
+     *            Example: <code>R.raw.south_south_east</code>
+     *
+     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     */
+    public int addSpeech(CharSequence text, String packagename, int resourceId) {
+        synchronized (mStartLock) {
+            mUtterances.put(text, makeResourceUri(packagename, resourceId));
+            return SUCCESS;
+        }
+    }
+
+    /**
      * Adds a mapping between a string of text and a sound file. Using this, it
      * is possible to add custom pronounciations for a string of text.
      * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
@@ -835,6 +880,28 @@
         }
     }
 
+    /**
+     * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file.
+     * Using this, it is possible to add custom pronounciations for a string of text.
+     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
+     * will play the specified sound resource if it is available, or synthesize the text it is
+     * missing.
+     *
+     * @param text
+     *            The string of text. Example: <code>"south_south_east"</code>
+     * @param filename
+     *            The full path to the sound file (for example:
+     *            "/sdcard/mysounds/hello.wav")
+     *
+     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     */
+    public int addSpeech(CharSequence text, String filename) {
+        synchronized (mStartLock) {
+            mUtterances.put(text, Uri.parse(filename));
+            return SUCCESS;
+        }
+    }
+
 
     /**
      * Adds a mapping between a string of text and a sound resource in a
@@ -899,6 +966,51 @@
     }
 
     /**
+     * Speaks the text using the specified queuing strategy and speech parameters, the text may
+     * be spanned with TtsSpans.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param text The string of text to be spoken. No longer than
+     *            {@link #getMaxSpeechInputLength()} characters.
+     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
+     *            {@link Engine#KEY_PARAM_VOLUME},
+     *            {@link Engine#KEY_PARAM_PAN}.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     * @param utteranceId An unique identifier for this request.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
+     */
+    public int speak(final CharSequence text,
+                     final int queueMode,
+                     final HashMap<String, String> params,
+                     final String utteranceId) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                Uri utteranceUri = mUtterances.get(text);
+                if (utteranceUri != null) {
+                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
+                            getParams(params), utteranceId);
+                } else {
+                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
+                            utteranceId);
+                }
+            }
+        }, ERROR, "speak");
+    }
+
+    /**
      * Speaks the string using the specified queuing strategy and speech parameters.
      * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
      * requests and then returns. The synthesis might not have finished (or even started!) at the
@@ -922,20 +1034,51 @@
      *            engine named "com.svox.pico" if it is being used.
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #speak(CharSequence, int, HashMap, String)}.
      */
+    @Deprecated
     public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+        return speak(text, queueMode, params,
+                     params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+    }
+
+    /**
+     * Plays the earcon using the specified queueing mode and parameters.
+     * The earcon must already have been added with {@link #addEarcon(String, String)} or
+     * {@link #addEarcon(String, String, int)}.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param earcon The earcon that should be played
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
+     */
+    public int playEarcon(final String earcon, final int queueMode,
+            final HashMap<String, String> params, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri utteranceUri = mUtterances.get(text);
-                if (utteranceUri != null) {
-                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
-                            getParams(params));
-                } else {
-                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params));
+                Uri earconUri = mEarcons.get(earcon);
+                if (earconUri == null) {
+                    return ERROR;
                 }
+                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
+                        getParams(params), utteranceId);
             }
-        }, ERROR, "speak");
+        }, ERROR, "playEarcon");
     }
 
     /**
@@ -961,20 +1104,46 @@
      *            engine named "com.svox.pico" if it is being used.
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #playEarcon(String, int, HashMap, String)}.
      */
+    @Deprecated
     public int playEarcon(final String earcon, final int queueMode,
             final HashMap<String, String> params) {
+        return playEarcon(earcon, queueMode, params,
+                          params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+    }
+
+    /**
+     * Plays silence for the specified amount of time using the specified
+     * queue mode.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param durationInMs The duration of the silence.
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     * @param utteranceId An unique identifier for this request.
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
+     */
+    public int playSilence(final long durationInMs, final int queueMode,
+            final HashMap<String, String> params, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri earconUri = mEarcons.get(earcon);
-                if (earconUri == null) {
-                    return ERROR;
-                }
-                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
-                        getParams(params));
+                return service.playSilence(getCallerIdentity(), durationInMs,
+                                           queueMode, utteranceId);
             }
-        }, ERROR, "playEarcon");
+        }, ERROR, "playSilence");
     }
 
     /**
@@ -998,16 +1167,14 @@
      *            engine named "com.svox.pico" if it is being used.
      *
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #playSilence(long, int, HashMap, String)}.
      */
+    @Deprecated
     public int playSilence(final long durationInMs, final int queueMode,
             final HashMap<String, String> params) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.playSilence(getCallerIdentity(), durationInMs, queueMode,
-                        params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
-            }
-        }, ERROR, "playSilence");
+        return playSilence(durationInMs, queueMode, params,
+                           params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
     }
 
     /**
@@ -1283,25 +1450,22 @@
      * requests and then returns. The synthesis might not have finished (or even started!) at the
      * time when this method returns. In order to reliably detect errors during synthesis,
      * we recommend setting an utterance progress listener (see
-     * {@link #setOnUtteranceProgressListener}) and using the
-     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     * {@link #setOnUtteranceProgressListener}).
      *
      * @param text The text that should be synthesized. No longer than
      *            {@link #getMaxSpeechInputLength()} characters.
      * @param params Parameters for the request. Can be null.
-     *            Supported parameter names:
-     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
      *            Engine specific parameters may be passed in but the parameter keys
      *            must be prefixed by the name of the engine they are intended for. For example
      *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
      *            engine named "com.svox.pico" if it is being used.
      * @param filename Absolute file filename to write the generated audio data to.It should be
      *            something like "/sdcard/myappsounds/mysound.wav".
-     *
+     * @param utteranceId An unique identifier for this request.
      * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
      */
-    public int synthesizeToFile(final String text, final HashMap<String, String> params,
-            final String filename) {
+    public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params,
+            final String filename, final String utteranceId) {
         return runAction(new Action<Integer>() {
             @Override
             public Integer run(ITextToSpeechService service) throws RemoteException {
@@ -1318,7 +1482,7 @@
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
                     returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
-                            fileDescriptor, getParams(params));
+                            fileDescriptor, getParams(params), utteranceId);
                     fileDescriptor.close();
                     return returnValue;
                 } catch (FileNotFoundException e) {
@@ -1332,10 +1496,41 @@
         }, ERROR, "synthesizeToFile");
     }
 
+    /**
+     * Synthesizes the given text to a file using the specified parameters.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}) and using the
+     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+     *
+     * @param text The text that should be synthesized. No longer than
+     *            {@link #getMaxSpeechInputLength()} characters.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+     *            engine named "com.svox.pico" if it is being used.
+     * @param filename Absolute file filename to write the generated audio data to.It should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     *
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
+     * @deprecated As of API level 20, replaced by
+     *         {@link #synthesizeToFile(CharSequence, HashMap, String, String)}.
+     */
+    public int synthesizeToFile(final String text, final HashMap<String, String> params,
+            final String filename) {
+        return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+    }
+
     private Bundle getParams(HashMap<String, String> params) {
         if (params != null && !params.isEmpty()) {
             Bundle bundle = new Bundle(mParams);
             copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
+            copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID);
             copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
             copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
             copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a53518f..017be93 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -17,6 +17,8 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -419,6 +421,73 @@
         public void dispatchOnError(int errorCode);
     }
 
+
+    /** Set of parameters affecting audio output. */
+    static class AudioOutputParams {
+        /**
+         * Audio session identifier. May be used to associate audio playback with one of the
+         * {@link android.media.audiofx.AudioEffect} objects. If not specified by client,
+         * it should be equal to {@link AudioSystem#AUDIO_SESSION_ALLOCATE}.
+         */
+        public final int mSessionId;
+
+        /**
+         * Audio stream type. Must be one of the STREAM_ contants defined in
+         * {@link android.media.AudioManager}.
+         */
+        public final int mStreamType;
+
+        /**
+         * Volume, in the range [0.0f, 1.0f]. The default value is
+         * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+         */
+        public final float mVolume;
+
+        /**
+         * Left/right position of the audio, in the range [-1.0f, 1.0f].
+         * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+         */
+        public final float mPan;
+
+        /** Create AudioOutputParams with default values */
+        AudioOutputParams() {
+            mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
+            mStreamType = Engine.DEFAULT_STREAM;
+            mVolume = Engine.DEFAULT_VOLUME;
+            mPan = Engine.DEFAULT_PAN;
+        }
+
+        AudioOutputParams(int sessionId, int streamType, float volume, float pan) {
+            mSessionId = sessionId;
+            mStreamType = streamType;
+            mVolume = volume;
+            mPan = pan;
+        }
+
+        /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
+        static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle) {
+            if (paramsBundle == null) {
+                return new AudioOutputParams();
+            }
+
+            return new AudioOutputParams(
+                    paramsBundle.getInt(
+                            Engine.KEY_PARAM_SESSION_ID,
+                            AudioSystem.AUDIO_SESSION_ALLOCATE),
+                    paramsBundle.getInt(
+                            Engine.KEY_PARAM_STREAM,
+                            Engine.DEFAULT_STREAM),
+                    paramsBundle.getFloat(
+                            Engine.KEY_PARAM_VOLUME,
+                            Engine.DEFAULT_VOLUME),
+                    paramsBundle.getFloat(
+                            Engine.KEY_PARAM_PAN,
+                            Engine.DEFAULT_PAN));
+        }
+
+    }
+
+
     /**
      * An item in the synth thread queue.
      */
@@ -568,11 +637,13 @@
      */
     private abstract class SpeechItemV1 extends UtteranceSpeechItem {
         protected final Bundle mParams;
+        protected final String mUtteranceId;
 
         SpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params) {
+                Bundle params, String utteranceId) {
             super(callerIdentity, callerUid, callerPid);
             mParams = params;
+            mUtteranceId = utteranceId;
         }
 
         boolean hasLanguage() {
@@ -589,25 +660,17 @@
 
         @Override
         public String getUtteranceId() {
-            return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null);
+            return mUtteranceId;
         }
 
-        int getStreamType() {
-            return getIntParam(mParams, Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
-        }
-
-        float getVolume() {
-            return getFloatParam(mParams, Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME);
-        }
-
-        float getPan() {
-            return getFloatParam(mParams, Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN);
+        AudioOutputParams getAudioParams() {
+            return AudioOutputParams.createFromV1ParamsBundle(mParams);
         }
     }
 
     class SynthesisSpeechItemV1 extends SpeechItemV1 {
         // Never null.
-        private final String mText;
+        private final CharSequence mText;
         private final SynthesisRequest mSynthesisRequest;
         private final String[] mDefaultLocale;
         // Non null after synthesis has started, and all accesses
@@ -617,8 +680,8 @@
         private final int mCallerUid;
 
         public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, String text) {
-            super(callerIdentity, callerUid, callerPid, params);
+                Bundle params, String utteranceId, CharSequence text) {
+            super(callerIdentity, callerUid, callerPid, params, utteranceId);
             mText = text;
             mCallerUid = callerUid;
             mSynthesisRequest = new SynthesisRequest(mText, mParams);
@@ -628,7 +691,7 @@
                     mPackageName);
         }
 
-        public String getText() {
+        public CharSequence getText() {
             return mText;
         }
 
@@ -668,7 +731,7 @@
         }
 
         protected AbstractSynthesisCallback createSynthesisCallback() {
-            return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(),
+            return new PlaybackSynthesisCallback(getAudioParams(),
                     mAudioPlaybackHandler, this, getCallerIdentity(), mEventLogger, false);
         }
 
@@ -713,8 +776,9 @@
         private final FileOutputStream mFileOutputStream;
 
         public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid,
-                int callerPid, Bundle params, String text, FileOutputStream fileOutputStream) {
-            super(callerIdentity, callerUid, callerPid, params, text);
+                int callerPid, Bundle params, String utteranceId, CharSequence text,
+                FileOutputStream fileOutputStream) {
+            super(callerIdentity, callerUid, callerPid, params, utteranceId, text);
             mFileOutputStream = fileOutputStream;
         }
 
@@ -740,10 +804,10 @@
         private final AudioPlaybackQueueItem mItem;
 
         public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
-                Bundle params, Uri uri) {
-            super(callerIdentity, callerUid, callerPid, params);
+                Bundle params, String utteranceId, Uri uri) {
+            super(callerIdentity, callerUid, callerPid, params, utteranceId);
             mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(),
-                    TextToSpeechService.this, uri, getStreamType());
+                    TextToSpeechService.this, uri, getAudioParams());
         }
 
         @Override
@@ -848,19 +912,20 @@
     // they can be used as message objects (which are tested for equality using ==).
     private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
         @Override
-        public int speak(IBinder caller, String text, int queueMode, Bundle params) {
+        public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params,
+                String utteranceId) {
             if (!checkNonNull(caller, text, params)) {
                 return TextToSpeech.ERROR;
             }
 
             SpeechItem item = new SynthesisSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, text);
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text);
             return mSynthHandler.enqueueSpeechItem(queueMode, item);
         }
 
         @Override
-        public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor
-                fileDescriptor, Bundle params) {
+        public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor
+                fileDescriptor, Bundle params, String utteranceId) {
             if (!checkNonNull(caller, text, fileDescriptor, params)) {
                 return TextToSpeech.ERROR;
             }
@@ -872,19 +937,20 @@
                     fileDescriptor.detachFd());
 
             SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, text,
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text,
                     new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor));
             return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
         }
 
         @Override
-        public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params) {
+        public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params,
+                String utteranceId) {
             if (!checkNonNull(caller, audioUri, params)) {
                 return TextToSpeech.ERROR;
             }
 
             SpeechItem item = new AudioSpeechItemV1(caller,
-                    Binder.getCallingUid(), Binder.getCallingPid(), params, audioUri);
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri);
             return mSynthHandler.enqueueSpeechItem(queueMode, item);
         }
 
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index fb4fd59..4144a75 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -379,7 +379,7 @@
                                 okBottom = fitBottom;
                         }
                     } else {
-                        final boolean moreChars = (j + 1 < spanEnd);
+                        final boolean moreChars;
                         int endPos;
                         int above, below, top, bottom;
                         float currentTextWidth;
@@ -391,6 +391,7 @@
                             top = okTop;
                             bottom = okBottom;
                             currentTextWidth = okWidth;
+                            moreChars = (j + 1 < spanEnd);
                         } else if (fit != here) {
                             endPos = fit;
                             above = fitAscent;
@@ -398,13 +399,21 @@
                             top = fitTop;
                             bottom = fitBottom;
                             currentTextWidth = fitWidth;
+                            moreChars = (j + 1 < spanEnd);
                         } else {
+                            // must make progress, so take next character
                             endPos = here + 1;
-                            above = fm.ascent;
-                            below = fm.descent;
-                            top = fm.top;
-                            bottom = fm.bottom;
+                            // but to deal properly with clusters
+                            // take all zero width characters following that
+                            while (endPos < spanEnd && widths[endPos - paraStart] == 0) {
+                                endPos++;
+                            }
+                            above = fmAscent;
+                            below = fmDescent;
+                            top = fmTop;
+                            bottom = fmBottom;
                             currentTextWidth = widths[here - paraStart];
+                            moreChars = (endPos < spanEnd);
                         }
 
                         v = out(source, here, endPos,
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index ba6f1d4..fcc3a40 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -20,6 +20,7 @@
 import android.text.Layout;
 import android.text.Selection;
 import android.text.Spannable;
+import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -221,11 +222,16 @@
         return lineEnd(widget, buffer);
     }
 
+    private static boolean isTouchSelecting(boolean isMouse, Spannable buffer) {
+        return isMouse ? Touch.isActivelySelecting(buffer) : isSelecting(buffer);
+    }
+
     @Override
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1;
         int initialScrollY = -1;
         final int action = event.getAction();
+        final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
 
         if (action == MotionEvent.ACTION_UP) {
             initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -236,19 +242,30 @@
 
         if (widget.isFocused() && !widget.didTouchFocusSelect()) {
             if (action == MotionEvent.ACTION_DOWN) {
-              if (isSelecting(buffer)) {
-                  int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-
-                  buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
-
-                  // Disallow intercepting of the touch events, so that
-                  // users can scroll and select at the same time.
-                  // without this, users would get booted out of select
-                  // mode once the view detected it needed to scroll.
-                  widget.getParent().requestDisallowInterceptTouchEvent(true);
-              }
+                // Capture the mouse pointer down location to ensure selection starts
+                // right under the mouse (and is not influenced by cursor location).
+                // The code below needs to run for mouse events.
+                // For touch events, the code should run only when selection is active.
+                if (isMouse || isTouchSelecting(isMouse, buffer)) {
+                    int offset = widget.getOffsetForPosition(event.getX(), event.getY());
+                    buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
+                    // Disallow intercepting of the touch events, so that
+                    // users can scroll and select at the same time.
+                    // without this, users would get booted out of select
+                    // mode once the view detected it needed to scroll.
+                    widget.getParent().requestDisallowInterceptTouchEvent(true);
+                }
             } else if (action == MotionEvent.ACTION_MOVE) {
-                if (isSelecting(buffer) && handled) {
+
+                // Cursor can be active at any location in the text while mouse pointer can start
+                // selection from a totally different location. Use LAST_TAP_DOWN span to ensure
+                // text selection will start from mouse pointer location.
+                if (isMouse && Touch.isSelectionStarted(buffer)) {
+                    int offset = buffer.getSpanStart(LAST_TAP_DOWN);
+                    Selection.setSelection(buffer, offset);
+                }
+
+                if (isTouchSelecting(isMouse, buffer) && handled) {
                     // Before selecting, make sure we've moved out of the "slop".
                     // handled will be true, if we're in select mode AND we're
                     // OUT of the slop
@@ -277,7 +294,7 @@
                 }
 
                 int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-                if (isSelecting(buffer)) {
+                if (isTouchSelecting(isMouse, buffer)) {
                     buffer.removeSpan(LAST_TAP_DOWN);
                     Selection.extendSelection(buffer, offset);
                 }
@@ -288,7 +305,6 @@
                 return true;
             }
         }
-
         return handled;
     }
 
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 9394a0b..fee7377 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -119,12 +119,18 @@
             ds = buffer.getSpans(0, buffer.length(), DragState.class);
 
             if (ds.length > 0) {
+                ds[0].mIsSelectionStarted = false;
+
                 if (ds[0].mFarEnough == false) {
                     int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
 
                     if (Math.abs(event.getX() - ds[0].mX) >= slop ||
                         Math.abs(event.getY() - ds[0].mY) >= slop) {
                         ds[0].mFarEnough = true;
+                        if (event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+                            ds[0].mIsActivelySelecting = true;
+                            ds[0].mIsSelectionStarted = true;
+                        }
                     }
                 }
 
@@ -135,9 +141,14 @@
                                     MetaKeyKeyListener.META_SHIFT_ON) == 1
                             || MetaKeyKeyListener.getMetaState(buffer,
                                     MetaKeyKeyListener.META_SELECTING) != 0;
+
+                    if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+                        ds[0].mIsActivelySelecting = false;
+                    }
+
                     float dx;
                     float dy;
-                    if (cap) {
+                    if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
                         // if we're selecting, we want the scroll to go in
                         // the direction of the drag
                         dx = event.getX() - ds[0].mX;
@@ -157,11 +168,13 @@
 
                     ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - padding));
                     ny = Math.max(ny, 0);
-        
+
                     int oldX = widget.getScrollX();
                     int oldY = widget.getScrollY();
 
-                    scrollTo(widget, layout, nx, ny);
+                    if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+                        scrollTo(widget, layout, nx, ny);
+                    }
 
                     // If we actually scrolled, then cancel the up action.
                     if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
@@ -194,6 +207,37 @@
         return ds.length > 0 ? ds[0].mScrollY : -1;
     }
 
+    /**
+     * Checks if selection is still active.
+     * This is useful for extending Selection span on buffer.
+     * @param buffer The text buffer.
+     * @return true if buffer has been marked for selection.
+     *
+     * @hide
+     */
+    static boolean isActivelySelecting(Spannable buffer) {
+        DragState[] ds;
+        ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+        return ds.length > 0 && ds[0].mIsActivelySelecting;
+    }
+
+    /**
+     * Checks if selection has begun (are we out of slop?).
+     * Note: DragState.mIsSelectionStarted goes back to false with the very next event.
+     * This is useful for starting Selection span on buffer.
+     * @param buffer The text buffer.
+     * @return true if selection has started on the buffer.
+     *
+     * @hide
+     */
+    static boolean isSelectionStarted(Spannable buffer) {
+        DragState[] ds;
+        ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+        return ds.length > 0 && ds[0].mIsSelectionStarted;
+    }
+
     private static class DragState implements NoCopySpan {
         public float mX;
         public float mY;
@@ -201,6 +245,8 @@
         public int mScrollY;
         public boolean mFarEnough;
         public boolean mUsed;
+        public boolean mIsActivelySelecting;
+        public boolean mIsSelectionStarted;
 
         public DragState(float x, float y, int scrollX, int scrollY) {
             mX = x;
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 9d4b720..8f7aefd 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -372,7 +372,7 @@
      * Perform a {@link #add(Object)} of all values in <var>array</var>
      * @param array The array whose contents are to be retrieved.
      */
-    public void putAll(ArraySet<? extends E> array) {
+    public void addAll(ArraySet<? extends E> array) {
         final int N = array.mSize;
         ensureCapacity(mSize + N);
         if (mSize == 0) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 1066430..f41afcf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -431,7 +431,7 @@
     /**
      * Gets the time when the current frame started.
      * <p>
-     * This method provides the time in nanoseconds when the frame started being rendered.
+     * This method provides the time in milliseconds when the frame started being rendered.
      * The frame time provides a stable time base for synchronizing animations
      * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
      * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 3670eed..3e7aae0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -46,7 +46,8 @@
     void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
 
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
-            in Rect visibleInsets, boolean reportDraw, in Configuration newConfig);
+            in Rect visibleInsets, in Rect stableInsets, boolean reportDraw,
+            in Configuration newConfig);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index fa5bd88..0f3f182 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -89,7 +89,7 @@
     int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility,
             int flags, out Rect outFrame, out Rect outOverscanInsets,
-            out Rect outContentInsets, out Rect outVisibleInsets,
+            out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Configuration outConfig, out Surface outSurface);
 
     /**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 7f2defda..ae39b7a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3131,6 +3131,24 @@
         return symbolicName != null ? symbolicName : Integer.toString(toolType);
     }
 
+    /**
+     * Checks if a mouse or stylus button (or combination of buttons) is pressed.
+     * @param button Button (or combination of buttons).
+     * @return True if specified buttons are pressed.
+     *
+     * @see #BUTTON_PRIMARY
+     * @see #BUTTON_SECONDARY
+     * @see #BUTTON_TERTIARY
+     * @see #BUTTON_FORWARD
+     * @see #BUTTON_BACK
+     */
+    public final boolean isButtonPressed(int button) {
+        if (button == 0) {
+            return false;
+        }
+        return (getButtonState() & button) == button;
+    }
+
     public static final Parcelable.Creator<MotionEvent> CREATOR
             = new Parcelable.Creator<MotionEvent>() {
         public MotionEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1363a5c..4b53c8e 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -18,6 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
@@ -34,7 +35,7 @@
 /**
  * @hide
  */
-public final class RenderNodeAnimator extends Animator {
+public class RenderNodeAnimator extends Animator {
     // Keep in sync with enum RenderProperty in Animator.h
     public static final int TRANSLATION_X = 0;
     public static final int TRANSLATION_Y = 1;
@@ -83,16 +84,23 @@
 
     private RenderNode mTarget;
     private View mViewTarget;
+    private int mRenderProperty = -1;
+    private float mFinalValue;
     private TimeInterpolator mInterpolator;
 
     private boolean mStarted = false;
     private boolean mFinished = false;
 
+    private long mUnscaledDuration = 300;
+    private long mUnscaledStartDelay = 0;
+
     public static int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
 
     public RenderNodeAnimator(int property, float finalValue) {
+        mRenderProperty = property;
+        mFinalValue = finalValue;
         init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
                 property, finalValue));
     }
@@ -156,7 +164,16 @@
 
         mStarted = true;
         applyInterpolator();
-        mTarget.addAnimator(this);
+        nStart(mNativePtr.get());
+
+        // Alpha is a special snowflake that has the canonical value stored
+        // in mTransformationInfo instead of in RenderNode, so we need to update
+        // it with the final value here.
+        if (mRenderProperty == RenderNodeAnimator.ALPHA) {
+            // Don't need null check because ViewPropertyAnimator's
+            // ctor calls ensureTransformationInfo()
+            mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
+        }
 
         final ArrayList<AnimatorListener> listeners = getListeners();
         final int numListeners = listeners == null ? 0 : listeners.size();
@@ -201,6 +218,7 @@
     public void setTarget(View view) {
         mViewTarget = view;
         mTarget = view.mRenderNode;
+        mTarget.addAnimator(this);
     }
 
     public void setTarget(Canvas canvas) {
@@ -213,12 +231,12 @@
     }
 
     public void setTarget(RenderNode node) {
+        if (mTarget != null) {
+            throw new IllegalStateException("Target already set!");
+        }
         mViewTarget = null;
         mTarget = node;
-    }
-
-    public RenderNode getTarget() {
-        return mTarget;
+        mTarget.addAnimator(this);
     }
 
     public void setStartValue(float startValue) {
@@ -232,12 +250,13 @@
         if (startDelay < 0) {
             throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
         }
-        nSetStartDelay(mNativePtr.get(), startDelay);
+        mUnscaledStartDelay = startDelay;
+        nSetStartDelay(mNativePtr.get(), (long) (startDelay * ValueAnimator.getDurationScale()));
     }
 
     @Override
     public long getStartDelay() {
-        return nGetStartDelay(mNativePtr.get());
+        return mUnscaledStartDelay;
     }
 
     @Override
@@ -246,13 +265,14 @@
         if (duration < 0) {
             throw new IllegalArgumentException("duration must be positive; " + duration);
         }
-        nSetDuration(mNativePtr.get(), duration);
+        mUnscaledDuration = duration;
+        nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
         return this;
     }
 
     @Override
     public long getDuration() {
-        return nGetDuration(mNativePtr.get());
+        return mUnscaledDuration;
     }
 
     @Override
@@ -307,5 +327,6 @@
     private static native long nGetStartDelay(long nativePtr);
     private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
 
+    private static native void nStart(long animPtr);
     private static native void nCancel(long animPtr);
 }
diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java
new file mode 100644
index 0000000..151277a
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimatorCompat.java
@@ -0,0 +1,144 @@
+/*
+ * 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.view;
+
+import android.animation.ValueAnimator;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides compatibility for things like start listeners &
+ * start delays for use by ViewPropertyAnimator and ObjectAnimator
+ * @hide
+ */
+public class RenderNodeAnimatorCompat extends RenderNodeAnimator {
+
+    private long mUnscaledStartDelay = 0;
+    private long mStartDelay = 0;
+    private long mStartTime;
+    private boolean mCanceled;
+
+    public RenderNodeAnimatorCompat(int property, float finalValue) {
+        super(property, finalValue);
+    }
+
+    @Override
+    public void setStartDelay(long startDelay) {
+        mUnscaledStartDelay = startDelay;
+        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+    }
+
+    @Override
+    public long getStartDelay() {
+        return mUnscaledStartDelay;
+    }
+
+    @Override
+    public void start() {
+        if (mStartDelay <= 0) {
+            doStart();
+        } else {
+            getHelper().addDelayedAnimation(this);
+        }
+    }
+
+    private void doStart() {
+        if (!mCanceled) {
+            super.start();
+        }
+    }
+
+    @Override
+    public void cancel() {
+        mCanceled = true;
+        super.cancel();
+    }
+
+    /**
+     * @return true if the animator was started, false if still delayed
+     */
+    private boolean processDelayed(long frameTimeMs) {
+        if (mCanceled) return true;
+
+        if (mStartTime == 0) {
+            mStartTime = frameTimeMs;
+        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+            doStart();
+            return true;
+        }
+        return false;
+    }
+
+    private static AnimationHelper getHelper() {
+        AnimationHelper helper = sAnimationHelper.get();
+        if (helper == null) {
+            helper = new AnimationHelper();
+            sAnimationHelper.set(helper);
+        }
+        return helper;
+    }
+
+    private static ThreadLocal<AnimationHelper> sAnimationHelper =
+            new ThreadLocal<AnimationHelper>();
+
+    private static class AnimationHelper implements Runnable {
+
+        private ArrayList<RenderNodeAnimatorCompat> mDelayedAnims = new ArrayList<RenderNodeAnimatorCompat>();
+        private final Choreographer mChoreographer;
+        private boolean mCallbackScheduled;
+
+        public AnimationHelper() {
+            mChoreographer = Choreographer.getInstance();
+        }
+
+        public void addDelayedAnimation(RenderNodeAnimatorCompat animator) {
+            mDelayedAnims.add(animator);
+            scheduleCallback();
+        }
+
+        private void scheduleCallback() {
+            if (!mCallbackScheduled) {
+                mCallbackScheduled = true;
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+            }
+        }
+
+        @Override
+        public void run() {
+            long frameTimeMs = mChoreographer.getFrameTime();
+            mCallbackScheduled = false;
+
+            int end = 0;
+            for (int i = 0; i < mDelayedAnims.size(); i++) {
+                RenderNodeAnimatorCompat animator = mDelayedAnims.get(i);
+                if (!animator.processDelayed(frameTimeMs)) {
+                    if (end != i) {
+                        mDelayedAnims.set(end, animator);
+                    }
+                    end++;
+                }
+            }
+            while (mDelayedAnims.size() > end) {
+                mDelayedAnims.remove(mDelayedAnims.size() - 1);
+            }
+
+            if (mDelayedAnims.size() > 0) {
+                scheduleCallback();
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4a2cc1a..a2a4540 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -105,6 +105,7 @@
     final Rect mWinFrame = new Rect();
     final Rect mOverscanInsets = new Rect();
     final Rect mContentInsets = new Rect();
+    final Rect mStableInsets = new Rect();
     final Configuration mConfiguration = new Configuration();
 
     static final int KEEP_SCREEN_ON_MSG = 1;
@@ -518,7 +519,7 @@
                             visible ? VISIBLE : GONE,
                             WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                             mWinFrame, mOverscanInsets, mContentInsets,
-                            mVisibleInsets, mConfiguration, mNewSurface);
+                            mVisibleInsets, mStableInsets, mConfiguration, mNewSurface);
                     if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                         mReportDrawNeeded = true;
                     }
@@ -653,7 +654,8 @@
 
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+                Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+                Configuration newConfig) {
             SurfaceView surfaceView = mSurfaceView.get();
             if (surfaceView != null) {
                 if (DEBUG) Log.v(
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d544804..3b2e1d1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7240,9 +7240,6 @@
             if (viewRootImpl != null) {
                 viewRootImpl.setAccessibilityFocus(this, null);
             }
-            Rect rect = (mAttachInfo != null) ? mAttachInfo.mTmpInvalRect : new Rect();
-            getDrawingRect(rect);
-            requestRectangleOnScreen(rect, false);
             invalidate();
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
             return true;
@@ -19808,6 +19805,13 @@
         final Rect mVisibleInsets = new Rect();
 
         /**
+         * For windows that are full-screen but using insets to layout inside
+         * of the screen decorations, these are the current insets for the
+         * stable system windows.
+         */
+        final Rect mStableInsets = new Rect();
+
+        /**
          * The internal insets given by this window.  This value is
          * supplied by the client (through
          * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 28e8bd6..36e5996 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6476,6 +6476,20 @@
         }
 
         /**
+         * @hide Used internally.
+         */
+        public final void copyMarginsFrom(MarginLayoutParams source) {
+            this.leftMargin = source.leftMargin;
+            this.topMargin = source.topMargin;
+            this.rightMargin = source.rightMargin;
+            this.bottomMargin = source.bottomMargin;
+            this.startMargin = source.startMargin;
+            this.endMargin = source.endMargin;
+
+            this.mMarginFlags = source.mMarginFlags;
+        }
+
+        /**
          * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
          * to be done so that the new margins are taken into account. Left and right margins may be
          * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 3f72b4c..4ca5863 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -338,7 +338,8 @@
      * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
      * will cause the declared object to be used instead.
      * 
-     * @param interpolator The TimeInterpolator to be used for ensuing property animations.
+     * @param interpolator The TimeInterpolator to be used for ensuing property animations. A value
+     * of <code>null</code> will result in linear interpolation.
      * @return This object, allowing calls to methods in this class to be chained.
      */
     public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 8b4277a..20f5182 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -18,6 +18,8 @@
 
 import android.animation.TimeInterpolator;
 import android.view.ViewPropertyAnimator.NameValuesHolder;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
 
 import com.android.internal.view.animation.FallbackLUTInterpolator;
 
@@ -29,6 +31,8 @@
  */
 class ViewPropertyAnimatorRT {
 
+    private static final Interpolator sLinearInterpolator = new LinearInterpolator();
+
     private final View mView;
 
     private RenderNodeAnimator mAnimators[] = new RenderNodeAnimator[RenderNodeAnimator.LAST_VALUE + 1];
@@ -65,6 +69,10 @@
         long startDelay = parent.getStartDelay();
         long duration = parent.getDuration();
         TimeInterpolator interpolator = parent.getInterpolator();
+        if (interpolator == null) {
+            // Documented to be LinearInterpolator in ValueAnimator.setInterpolator
+            interpolator = sLinearInterpolator;
+        }
         if (!RenderNodeAnimator.isNativeInterpolator(interpolator)) {
             interpolator = new FallbackLUTInterpolator(interpolator, duration);
         }
@@ -73,21 +81,14 @@
             int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
 
             final float finalValue = holder.mFromValue + holder.mDeltaValue;
-            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
+            RenderNodeAnimator animator = new RenderNodeAnimatorCompat(property, finalValue);
             animator.setStartDelay(startDelay);
             animator.setDuration(duration);
             animator.setInterpolator(interpolator);
             animator.setTarget(mView);
             animator.start();
 
-            // Alpha is a special snowflake that has the canonical value stored
-            // in mTransformationInfo instead of in RenderNode, so we need to update
-            // it with the final value here.
-            if (property == RenderNodeAnimator.ALPHA) {
-                // Don't need null check because ViewPropertyAnimator's
-                // ctor calls ensureTransformationInfo()
-                parent.mView.mTransformationInfo.mAlpha = finalValue;
-            }
+            mAnimators[property] = animator;
         }
 
         parent.mPendingAnimations.clear();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3219330..5def940 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,7 +21,6 @@
 import android.app.ActivityManagerNative;
 import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
-import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -205,7 +204,7 @@
     /** Set to true while in performTraversals for detecting when die(true) is called from internal
      * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */
     boolean mIsInTraversal;
-    boolean mFitSystemWindowsRequested;
+    boolean mApplyInsetsRequested;
     boolean mLayoutRequested;
     boolean mFirst;
     boolean mReportNextDraw;
@@ -257,11 +256,13 @@
 
     final Rect mPendingOverscanInsets = new Rect();
     final Rect mPendingVisibleInsets = new Rect();
+    final Rect mPendingStableInsets = new Rect();
     final Rect mPendingContentInsets = new Rect();
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
-    final Rect mFitSystemWindowsInsets = new Rect();
+    final Rect mDispatchContentInsets = new Rect();
+    final Rect mDispatchStableInsets = new Rect();
 
     final Configuration mLastConfiguration = new Configuration();
     final Configuration mPendingConfiguration = new Configuration();
@@ -532,6 +533,7 @@
                 }
                 mPendingOverscanInsets.set(0, 0, 0, 0);
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
+                mPendingStableInsets.set(mAttachInfo.mStableInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
@@ -816,7 +818,7 @@
     @Override
     public void requestFitSystemWindows() {
         checkThread();
-        mFitSystemWindowsRequested = true;
+        mApplyInsetsRequested = true;
         scheduleTraversals();
     }
 
@@ -1161,7 +1163,8 @@
     }
 
     void dispatchApplyInsets(View host) {
-        mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
+        mDispatchContentInsets.set(mAttachInfo.mContentInsets);
+        mDispatchStableInsets.set(mAttachInfo.mStableInsets);
         boolean isRound = false;
         if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0
                 && mDisplay.getDisplayId() == 0) {
@@ -1171,7 +1174,8 @@
                             com.android.internal.R.bool.config_windowIsRound);
         }
         host.dispatchApplyWindowInsets(new WindowInsets(
-                mFitSystemWindowsInsets, isRound));
+                mDispatchContentInsets, null /* windowDecorInsets */,
+                mDispatchStableInsets, isRound));
     }
 
     private void performTraversals() {
@@ -1310,6 +1314,9 @@
                 if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
                     insetsChanged = true;
                 }
+                if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
+                    insetsChanged = true;
+                }
                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                     if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
@@ -1383,8 +1390,8 @@
                     & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
         }
 
-        if (mFitSystemWindowsRequested) {
-            mFitSystemWindowsRequested = false;
+        if (mApplyInsetsRequested) {
+            mApplyInsetsRequested = false;
             mLastOverscanRequested = mAttachInfo.mOverscanRequested;
             dispatchApplyInsets(host);
             if (mLayoutRequested) {
@@ -1469,6 +1476,7 @@
                         + " overscan=" + mPendingOverscanInsets.toShortString()
                         + " content=" + mPendingContentInsets.toShortString()
                         + " visible=" + mPendingVisibleInsets.toShortString()
+                        + " visible=" + mPendingStableInsets.toShortString()
                         + " surface=" + mSurface);
 
                 if (mPendingConfiguration.seq != 0) {
@@ -1484,6 +1492,8 @@
                         mAttachInfo.mContentInsets);
                 final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
                         mAttachInfo.mVisibleInsets);
+                final boolean stableInsetsChanged = !mPendingStableInsets.equals(
+                        mAttachInfo.mStableInsets);
                 if (contentInsetsChanged) {
                     if (mWidth > 0 && mHeight > 0 && lp != null &&
                             ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
@@ -1558,12 +1568,19 @@
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
                 }
+                if (stableInsetsChanged) {
+                    mAttachInfo.mStableInsets.set(mPendingStableInsets);
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Decor insets changing to: "
+                            + mAttachInfo.mStableInsets);
+                    // Need to relayout with content insets.
+                    contentInsetsChanged = true;
+                }
                 if (contentInsetsChanged || mLastSystemUiVisibility !=
-                        mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested
+                        mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
                         || mLastOverscanRequested != mAttachInfo.mOverscanRequested) {
                     mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
                     mLastOverscanRequested = mAttachInfo.mOverscanRequested;
-                    mFitSystemWindowsRequested = false;
+                    mApplyInsetsRequested = false;
                     dispatchApplyInsets(host);
                 }
                 if (visibleInsetsChanged) {
@@ -3065,6 +3082,7 @@
                 if (mWinFrame.equals(args.arg1)
                         && mPendingOverscanInsets.equals(args.arg5)
                         && mPendingContentInsets.equals(args.arg2)
+                        && mPendingStableInsets.equals(args.arg6)
                         && mPendingVisibleInsets.equals(args.arg3)
                         && args.arg4 == null) {
                     break;
@@ -3082,6 +3100,7 @@
                     mWinFrame.set((Rect) args.arg1);
                     mPendingOverscanInsets.set((Rect) args.arg5);
                     mPendingContentInsets.set((Rect) args.arg2);
+                    mPendingStableInsets.set((Rect) args.arg6);
                     mPendingVisibleInsets.set((Rect) args.arg3);
 
                     args.recycle();
@@ -5167,7 +5186,7 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingConfiguration, mSurface);
+                mPendingStableInsets, mPendingConfiguration, mSurface);
         //Log.d(TAG, "<<<<<< BACK FROM relayout");
         if (restore) {
             params.restore();
@@ -5178,6 +5197,7 @@
             mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
+            mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
         }
         return relayoutResult;
     }
@@ -5446,7 +5466,7 @@
     }
 
     public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
-            Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+            Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) {
         if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -5465,6 +5485,7 @@
         args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
         args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig;
         args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
+        args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
         msg.obj = args;
         mHandler.sendMessage(msg);
     }
@@ -6292,11 +6313,12 @@
 
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+                Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+                Configuration newConfig) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
-                        visibleInsets, reportDraw, newConfig);
+                        visibleInsets, stableInsets, reportDraw, newConfig);
             }
         }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0a44d23..aa71ed8 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -130,7 +130,20 @@
     public static final int PROGRESS_SECONDARY_START = 20000;
     /** Highest possible value for the secondary progress */
     public static final int PROGRESS_SECONDARY_END = 30000;
-    
+
+    /**
+     * The transitionName for the status bar background View when a custom background is used.
+     * @see android.view.Window#setStatusBarColor(int)
+     */
+    public static final String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
+
+    /**
+     * The transitionName for the navigation bar background View when a custom background is used.
+     * @see android.view.Window#setNavigationBarColor(int)
+     */
+    public static final String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME =
+            "android:navigation:background";
+
     /** The default features enabled */
     @SuppressWarnings({"PointlessBitwiseExpression"})
     protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) |
@@ -1554,6 +1567,9 @@
      * If {@param color} is not opaque, consider setting
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
+     * <p>
+     * The transitionName for the view background will be "android:status:background".
+     * </p>
      */
     public abstract void setStatusBarColor(int color);
 
@@ -1573,6 +1589,9 @@
      * If {@param color} is not opaque, consider setting
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
+     * <p>
+     * The transitionName for the view background will be "android:navigation:background".
+     * </p>
      */
     public abstract void setNavigationBarColor(int color);
 
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 1d2f1bf..1832cc3 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -30,13 +30,16 @@
  * @see View#onApplyWindowInsets(WindowInsets)
  */
 public final class WindowInsets {
+
     private Rect mSystemWindowInsets;
     private Rect mWindowDecorInsets;
+    private Rect mStableInsets;
     private Rect mTempRect;
     private boolean mIsRound;
 
     private boolean mSystemWindowInsetsConsumed = false;
     private boolean mWindowDecorInsetsConsumed = false;
+    private boolean mStableInsetsConsumed = false;
 
     private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
 
@@ -49,29 +52,21 @@
     public static final WindowInsets CONSUMED;
 
     static {
-        CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT);
-        CONSUMED.mSystemWindowInsetsConsumed = true;
-        CONSUMED.mWindowDecorInsetsConsumed = true;
+        CONSUMED = new WindowInsets(null, null, null, false);
     }
 
     /** @hide */
-    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) {
-        this(systemWindowInsets, windowDecorInsets, false);
-    }
-
-    /** @hide */
-    public WindowInsets(Rect systemWindowInsets, boolean isRound) {
-        this(systemWindowInsets, null, isRound);
-    }
-
-    /** @hide */
-    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) {
+    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
+            boolean isRound) {
         mSystemWindowInsetsConsumed = systemWindowInsets == null;
         mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
 
         mWindowDecorInsetsConsumed = windowDecorInsets == null;
         mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets;
 
+        mStableInsetsConsumed = stableInsets == null;
+        mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : stableInsets;
+
         mIsRound = isRound;
     }
 
@@ -83,14 +78,16 @@
     public WindowInsets(WindowInsets src) {
         mSystemWindowInsets = src.mSystemWindowInsets;
         mWindowDecorInsets = src.mWindowDecorInsets;
+        mStableInsets = src.mStableInsets;
         mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed;
         mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
+        mStableInsetsConsumed = src.mStableInsetsConsumed;
         mIsRound = src.mIsRound;
     }
 
     /** @hide */
     public WindowInsets(Rect systemWindowInsets) {
-        this(systemWindowInsets, null);
+        this(systemWindowInsets, null, null, false);
     }
 
     /**
@@ -272,7 +269,7 @@
      * @hide Pending API
      */
     public boolean isConsumed() {
-        return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed;
+        return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed;
     }
 
     /**
@@ -381,9 +378,57 @@
         return result;
     }
 
+    /**
+     * @hide
+     */
+    public int getStableInsetTop() {
+        return mStableInsets.top;
+    }
+
+    /**
+     * @hide
+     */
+    public int getStableInsetLeft() {
+        return mStableInsets.left;
+    }
+
+    /**
+     * @hide
+     */
+    public int getStableInsetRight() {
+        return mStableInsets.right;
+    }
+
+    /**
+     * @hide
+     */
+    public int getStableInsetBottom() {
+        return mStableInsets.bottom;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean hasStableInsets() {
+        return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
+                || mStableInsets.bottom != 0;
+    }
+
+    /**
+     * @hide
+     */
+    public WindowInsets consumeStableInsets() {
+        final WindowInsets result = new WindowInsets(this);
+        result.mStableInsets = EMPTY_RECT;
+        result.mStableInsetsConsumed = true;
+        return result;
+    }
+
     @Override
     public String toString() {
-        return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets + " windowDecorInsets=" +
-                mWindowDecorInsets + (isRound() ? "round}" : "}");
+        return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
+                + " windowDecorInsets=" + mWindowDecorInsets
+                + " stableInsets=" + mStableInsets +
+                (isRound() ? " round}" : "}");
     }
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 024600d..ee542a1 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -149,9 +149,11 @@
          * are visible.
          * @param decorFrame The decor frame specified by policy specific to this window,
          * to use for proper cropping during animation.
+         * @param stableFrame The frame around which stable system decoration is positioned.
          */
         public void computeFrameLw(Rect parentFrame, Rect displayFrame,
-                Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame);
+                Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
+                Rect stableFrame);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index d3d5fff..a22931a 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -291,15 +291,30 @@
          */
         public Builder setMatrix(final Matrix matrix) {
             mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX);
+            mMatrixInitialized = true;
             return this;
         }
         private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
+        private boolean mMatrixInitialized = false;
 
         /**
-         * @return {@link CursorAnchorInfo} using parameters in this
-         * {@link Builder}.
+         * @return {@link CursorAnchorInfo} using parameters in this {@link Builder}.
+         * @throws IllegalArgumentException if one or more positional parameters are specified but
+         * the coordinate transformation matrix is not provided via {@link #setMatrix(Matrix)}.
          */
         public CursorAnchorInfo build() {
+            if (!mMatrixInitialized) {
+                // Coordinate transformation matrix is mandatory when positional parameters are
+                // specified.
+                if ((mCharacterRectBuilder != null && !mCharacterRectBuilder.isEmpty()) ||
+                        !Float.isNaN(mInsertionMarkerHorizontal) ||
+                        !Float.isNaN(mInsertionMarkerTop) ||
+                        !Float.isNaN(mInsertionMarkerBaseline) ||
+                        !Float.isNaN(mInsertionMarkerBottom)) {
+                    throw new IllegalArgumentException("Coordinate transformation matrix is " +
+                            "required when positional parameters are specified.");
+                }
+            }
             return new CursorAnchorInfo(this);
         }
 
@@ -317,6 +332,7 @@
             mInsertionMarkerBaseline = Float.NaN;
             mInsertionMarkerBottom = Float.NaN;
             mMatrix.set(Matrix.IDENTITY_MATRIX);
+            mMatrixInitialized = false;
             if (mCharacterRectBuilder != null) {
                 mCharacterRectBuilder.reset();
             }
diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java
index 40cade7..d4ec9d0 100644
--- a/core/java/android/view/inputmethod/SparseRectFArray.java
+++ b/core/java/android/view/inputmethod/SparseRectFArray.java
@@ -196,6 +196,10 @@
         private float[] mCoordinates = null;
         private static int INITIAL_SIZE = 16;
 
+        public boolean isEmpty() {
+            return mCount <= 0;
+        }
+
         /**
          * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}.
          */
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 50cdc0e..8f8876b 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2495,6 +2495,17 @@
     }
 
     /**
+     * Positions the selector in a way that mimics touch.
+     */
+    void positionSelectorLikeTouch(int position, View sel, float x, float y) {
+        positionSelectorLikeFocus(position, sel);
+
+        if (mSelector != null && position != INVALID_POSITION) {
+            mSelector.setHotspot(x, y);
+        }
+    }
+
+    /**
      * Positions the selector in a way that mimics keyboard focus.
      */
     void positionSelectorLikeFocus(int position, View sel) {
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 1fddf3e..7123b9a 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -668,7 +668,8 @@
         private SubMenuBuilder mSubMenu;
 
         public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
-            super(context, subMenu);
+            super(context, subMenu, null, false,
+                    com.android.internal.R.attr.actionOverflowMenuStyle);
             mSubMenu = subMenu;
 
             MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index f91865b..84b213b 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1363,7 +1363,13 @@
             // Forward converted event to destination view, then recycle it.
             final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId);
             dstEvent.recycle();
-            return handled;
+
+            // Always cancel forwarding when the touch stream ends.
+            final int action = srcEvent.getActionMasked();
+            final boolean keepForwarding = action != MotionEvent.ACTION_UP
+                    && action != MotionEvent.ACTION_CANCEL;
+
+            return handled && keepForwarding;
         }
 
         private class DisallowIntercept implements Runnable {
@@ -1492,7 +1498,7 @@
                     }
 
                     final View child = getChildAt(position - getFirstVisiblePosition());
-                    setPressedItem(child, position);
+                    setPressedItem(child, position, x, y);
                     handledEvent = true;
 
                     if (actionMasked == MotionEvent.ACTION_UP) {
@@ -1555,7 +1561,7 @@
             }
         }
 
-        private void setPressedItem(View child, int position) {
+        private void setPressedItem(View child, int position, float x, float y) {
             mDrawsInPressedState = true;
 
             // Ordering is essential. First update the pressed state and layout
@@ -1565,7 +1571,7 @@
 
             // Ensure that keyboard focus starts from the last touched position.
             setSelectedPositionInt(position);
-            positionSelectorLikeFocus(position, child);
+            positionSelectorLikeTouch(position, child, x, y);
 
             // Refresh the drawable state to reflect the new pressed state,
             // which will also update the selector state.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8567bed..5743882 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -37,8 +37,6 @@
 import android.inputmethodservice.ExtractEditText;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -97,6 +95,7 @@
 import android.util.TypedValue;
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.ActionMode;
+import android.view.Choreographer;
 import android.view.DragEvent;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -9088,26 +9087,22 @@
         }
     }
 
-    private static final class Marquee extends Handler {
+    private static final class Marquee {
         // TODO: Add an option to configure this
         private static final float MARQUEE_DELTA_MAX = 0.07f;
         private static final int MARQUEE_DELAY = 1200;
         private static final int MARQUEE_RESTART_DELAY = 1200;
-        private static final int MARQUEE_RESOLUTION = 1000 / 30;
-        private static final int MARQUEE_PIXELS_PER_SECOND = 30;
+        private static final int MARQUEE_DP_PER_SECOND = 30;
 
         private static final byte MARQUEE_STOPPED = 0x0;
         private static final byte MARQUEE_STARTING = 0x1;
         private static final byte MARQUEE_RUNNING = 0x2;
 
-        private static final int MESSAGE_START = 0x1;
-        private static final int MESSAGE_TICK = 0x2;
-        private static final int MESSAGE_RESTART = 0x3;
-
         private final WeakReference<TextView> mView;
+        private final Choreographer mChoreographer;
 
         private byte mStatus = MARQUEE_STOPPED;
-        private final float mScrollUnit;
+        private final float mPixelsPerSecond;
         private float mMaxScroll;
         private float mMaxFadeScroll;
         private float mGhostStart;
@@ -9116,49 +9111,62 @@
         private int mRepeatLimit;
 
         private float mScroll;
+        private long mLastAnimationMs;
 
         Marquee(TextView v) {
             final float density = v.getContext().getResources().getDisplayMetrics().density;
-            mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION;
+            mPixelsPerSecond = MARQUEE_DP_PER_SECOND * density;
             mView = new WeakReference<TextView>(v);
+            mChoreographer = Choreographer.getInstance();
         }
 
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_START:
-                    mStatus = MARQUEE_RUNNING;
-                    tick();
-                    break;
-                case MESSAGE_TICK:
-                    tick();
-                    break;
-                case MESSAGE_RESTART:
-                    if (mStatus == MARQUEE_RUNNING) {
-                        if (mRepeatLimit >= 0) {
-                            mRepeatLimit--;
-                        }
-                        start(mRepeatLimit);
-                    }
-                    break;
+        private Choreographer.FrameCallback mTickCallback = new Choreographer.FrameCallback() {
+            @Override
+            public void doFrame(long frameTimeNanos) {
+                tick();
             }
-        }
+        };
+
+        private Choreographer.FrameCallback mStartCallback = new Choreographer.FrameCallback() {
+            @Override
+            public void doFrame(long frameTimeNanos) {
+                mStatus = MARQUEE_RUNNING;
+                mLastAnimationMs = mChoreographer.getFrameTime();
+                tick();
+            }
+        };
+
+        private Choreographer.FrameCallback mRestartCallback = new Choreographer.FrameCallback() {
+            @Override
+            public void doFrame(long frameTimeNanos) {
+                if (mStatus == MARQUEE_RUNNING) {
+                    if (mRepeatLimit >= 0) {
+                        mRepeatLimit--;
+                    }
+                    start(mRepeatLimit);
+                }
+            }
+        };
 
         void tick() {
             if (mStatus != MARQUEE_RUNNING) {
                 return;
             }
 
-            removeMessages(MESSAGE_TICK);
+            mChoreographer.removeFrameCallback(mTickCallback);
 
             final TextView textView = mView.get();
             if (textView != null && (textView.isFocused() || textView.isSelected())) {
-                mScroll += mScrollUnit;
+                long currentMs = mChoreographer.getFrameTime();
+                long deltaMs = currentMs - mLastAnimationMs;
+                mLastAnimationMs = currentMs;
+                float deltaPx = deltaMs / 1000f * mPixelsPerSecond;
+                mScroll += deltaPx;
                 if (mScroll > mMaxScroll) {
                     mScroll = mMaxScroll;
-                    sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY);
+                    mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);
                 } else {
-                    sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION);
+                    mChoreographer.postFrameCallback(mTickCallback);
                 }
                 textView.invalidate();
             }
@@ -9166,9 +9174,9 @@
 
         void stop() {
             mStatus = MARQUEE_STOPPED;
-            removeMessages(MESSAGE_START);
-            removeMessages(MESSAGE_RESTART);
-            removeMessages(MESSAGE_TICK);
+            mChoreographer.removeFrameCallback(mStartCallback);
+            mChoreographer.removeFrameCallback(mRestartCallback);
+            mChoreographer.removeFrameCallback(mTickCallback);
             resetScroll();
         }
 
@@ -9199,7 +9207,7 @@
                 mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;
 
                 textView.invalidate();
-                sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY);
+                mChoreographer.postFrameCallback(mStartCallback);
             }
         }
 
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 122df2c..24950f6 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1233,7 +1233,7 @@
         }
         if (layoutSubtitle) {
             final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
-            titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
+            titleHeight += lp.topMargin + mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin;
         }
 
         if (layoutTitle || layoutSubtitle) {
@@ -1585,6 +1585,10 @@
     /**
      * Layout information for child views of Toolbars.
      *
+     * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
+     * ActionBar API. See {@link android.app.Activity#setActionBar(Toolbar) Activity.setActionBar}
+     * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
+     *
      * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
      */
     public static class LayoutParams extends ActionBar.LayoutParams {
@@ -1624,6 +1628,9 @@
 
         public LayoutParams(MarginLayoutParams source) {
             super(source);
+            // ActionBar.LayoutParams doesn't have a MarginLayoutParams constructor.
+            // Fake it here and copy over the relevant data.
+            copyMarginsFrom(source);
         }
 
         public LayoutParams(ViewGroup.LayoutParams source) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 7a9137b..ae9b515 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -58,6 +58,8 @@
 
     void noteVibratorOn(int uid, long durationMillis);
     void noteVibratorOff(int uid);
+    void noteFlashlightOn();
+    void noteFlashlightOff();
     void noteStartGps(int uid);
     void noteStopGps(int uid);
     void noteScreenState(int state);
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index ec2d654..7acf4d3 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -37,6 +37,8 @@
 
 import java.text.Collator;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.ArrayList;
 
@@ -108,8 +110,9 @@
             final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
         final Resources resources = context.getResources();
 
-        ArrayList<String> localeList = new ArrayList<String>(Arrays.asList(
-                Resources.getSystem().getAssets().getLocales()));
+        String[] locales = Resources.getSystem().getAssets().getLocales();
+        List<String> localeList = new ArrayList<String>(locales.length);
+        Collections.addAll(localeList, locales);
         if (isInDeveloperMode) {
             if (!localeList.contains("zz_ZZ")) {
                 localeList.add("zz_ZZ");
@@ -120,78 +123,61 @@
          *  }
          */
         }
-        String[] locales = new String[localeList.size()];
-        locales = localeList.toArray(locales);
 
+        Collections.sort(localeList);
         final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
         final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
-        Arrays.sort(locales);
-        final int origSize = locales.length;
-        final LocaleInfo[] preprocess = new LocaleInfo[origSize];
-        int finalSize = 0;
-        for (int i = 0 ; i < origSize; i++ ) {
-            final String s = locales[i];
-            final int len = s.length();
-            if (len == 5) {
-                String language = s.substring(0, 2);
-                String country = s.substring(3, 5);
-                final Locale l = new Locale(language, country);
 
-                if (finalSize == 0) {
+        final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
+        for (String locale : localeList) {
+            final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
+            if (l == null || "und".equals(l.getLanguage())) {
+                continue;
+            }
+
+            if (localeInfos.isEmpty()) {
+                if (DEBUG) {
+                    Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+                }
+                localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
+            } else {
+                // check previous entry:
+                //  same lang and a country -> upgrade to full name and
+                //    insert ours with full name
+                //  diff lang -> insert ours with lang-only name
+                final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
+                if (previous.locale.getLanguage().equals(l.getLanguage()) &&
+                        !previous.locale.getLanguage().equals("zz")) {
                     if (DEBUG) {
-                        Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+                        Log.v(TAG, "backing up and fixing " + previous.label + " to " +
+                                getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
                     }
-                    preprocess[finalSize++] =
-                            new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
+                    previous.label = toTitleCase(getDisplayName(
+                            previous.locale, specialLocaleCodes, specialLocaleNames));
+                    if (DEBUG) {
+                        Log.v(TAG, "  and adding "+ toTitleCase(
+                                getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
+                    }
+                    localeInfos.add(new LocaleInfo(toTitleCase(
+                            getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
                 } else {
-                    // check previous entry:
-                    //  same lang and a country -> upgrade to full name and
-                    //    insert ours with full name
-                    //  diff lang -> insert ours with lang-only name
-                    if (preprocess[finalSize-1].locale.getLanguage().equals(
-                            language) &&
-                            !preprocess[finalSize-1].locale.getLanguage().equals("zz")) {
-                        if (DEBUG) {
-                            Log.v(TAG, "backing up and fixing "+
-                                    preprocess[finalSize-1].label+" to "+
-                                    getDisplayName(preprocess[finalSize-1].locale,
-                                            specialLocaleCodes, specialLocaleNames));
-                        }
-                        preprocess[finalSize-1].label = toTitleCase(
-                                getDisplayName(preprocess[finalSize-1].locale,
-                                        specialLocaleCodes, specialLocaleNames));
-                        if (DEBUG) {
-                            Log.v(TAG, "  and adding "+ toTitleCase(
-                                    getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
-                        }
-                        preprocess[finalSize++] =
-                                new LocaleInfo(toTitleCase(
-                                        getDisplayName(
-                                                l, specialLocaleCodes, specialLocaleNames)), l);
+                    String displayName;
+                    if (locale.equals("zz_ZZ")) {
+                        displayName = "[Developer] Accented English";
+                    } else if (locale.equals("zz_ZY")) {
+                        displayName = "[Developer] Fake Bi-Directional";
                     } else {
-                        String displayName;
-                        if (s.equals("zz_ZZ")) {
-                            displayName = "[Developer] Accented English";
-                        } else if (s.equals("zz_ZY")) {
-                            displayName = "[Developer] Fake Bi-Directional";
-                        } else {
-                            displayName = toTitleCase(l.getDisplayLanguage(l));
-                        }
-                        if (DEBUG) {
-                            Log.v(TAG, "adding "+displayName);
-                        }
-                        preprocess[finalSize++] = new LocaleInfo(displayName, l);
+                        displayName = toTitleCase(l.getDisplayLanguage(l));
                     }
+                    if (DEBUG) {
+                        Log.v(TAG, "adding "+displayName);
+                    }
+                    localeInfos.add(new LocaleInfo(displayName, l));
                 }
             }
         }
 
-        final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
-        for (int i = 0; i < finalSize; i++) {
-            localeInfos[i] = preprocess[i];
-        }
-        Arrays.sort(localeInfos);
-
+        Collections.sort(localeInfos);
         final LayoutInflater inflater =
                 (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index dab3aff..832829d 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -19,6 +19,7 @@
 import android.content.pm.PackageManager;
 import android.util.Slog;
 
+import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 
@@ -39,20 +40,29 @@
      *
      * @hide
      */
-    public static class ApkHandle {
+    public static class ApkHandle implements Closeable {
         final String apkPath;
         final long apkHandle;
 
-        public ApkHandle(String path) {
-            apkPath = path;
-            apkHandle = nativeOpenApk(apkPath);
+        public static ApkHandle create(String path) throws IOException {
+            final long handle = nativeOpenApk(path);
+            if (handle == 0) {
+                throw new IOException("Unable to open APK: " + path);
+            }
+
+            return new ApkHandle(path, handle);
         }
 
-        public ApkHandle(File apkFile) {
-            apkPath = apkFile.getPath();
-            apkHandle = nativeOpenApk(apkPath);
+        public static ApkHandle create(File path) throws IOException {
+            return create(path.getAbsolutePath());
         }
 
+        private ApkHandle(String apkPath, long apkHandle) {
+            this.apkPath = apkPath;
+            this.apkHandle = apkHandle;
+        }
+
+        @Override
         public void close() {
             nativeClose(apkHandle);
         }
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 6ca24d7..247c8fe 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -54,6 +54,7 @@
         PHONE,
         WIFI,
         BLUETOOTH,
+        FLASHLIGHT,
         SCREEN,
         APP,
         USER,
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 7ff949e..469aa6f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -706,6 +706,15 @@
         }
     }
 
+    private void addFlashlightUsage() {
+        long flashlightOnTimeMs = mStats.getFlashlightOnTime(mRawRealtime, mStatsType) / 1000;
+        double flashlightPower = flashlightOnTimeMs
+                * mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT) / (60*60*1000);
+        if (flashlightPower != 0) {
+            addEntry(BatterySipper.DrainType.FLASHLIGHT, flashlightOnTimeMs, flashlightPower);
+        }
+    }
+
     private void addUserUsage() {
         for (int i=0; i<mUserSippers.size(); i++) {
             final int userId = mUserSippers.keyAt(i);
@@ -760,6 +769,7 @@
         addUserUsage();
         addPhoneUsage();
         addScreenUsage();
+        addFlashlightUsage();
         addWiFiUsage();
         addBluetoothUsage();
         addIdleUsage(); // Not including cellular idle power
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 02e4b3f..49d11dc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -89,7 +89,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 107 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 108 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -260,6 +260,9 @@
     boolean mVideoOn;
     StopwatchTimer mVideoOnTimer;
 
+    boolean mFlashlightOn;
+    StopwatchTimer mFlashlightOnTimer;
+
     int mPhoneSignalStrengthBin = -1;
     int mPhoneSignalStrengthBinRaw = -1;
     final StopwatchTimer[] mPhoneSignalStrengthsTimer = 
@@ -3177,6 +3180,32 @@
         getUidStatsLocked(uid).noteVibratorOffLocked();
     }
 
+    public void noteFlashlightOnLocked() {
+        if (!mFlashlightOn) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mFlashlightOn = true;
+            mFlashlightOnTimer.startRunningLocked(elapsedRealtime);
+        }
+    }
+
+    public void noteFlashlightOffLocked() {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (mFlashlightOn) {
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mFlashlightOn = false;
+            mFlashlightOnTimer.stopRunningLocked(elapsedRealtime);
+        }
+    }
+
     public void noteWifiRunningLocked(WorkSource ws) {
         if (!mGlobalWifiRunning) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -3682,6 +3711,14 @@
         return mBluetoothStateTimer[bluetoothState].getCountLocked(which);
     }
 
+    @Override public long getFlashlightOnTime(long elapsedRealtimeUs, int which) {
+        return mFlashlightOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+    }
+
+    @Override public long getFlashlightOnCount(int which) {
+        return mFlashlightOnTimer.getCountLocked(which);
+    }
+
     @Override
     public long getNetworkActivityBytes(int type, int which) {
         if (type >= 0 && type < mNetworkByteActivityCounters.length) {
@@ -5681,6 +5718,7 @@
         }
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
+        mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
         mOnBattery = mOnBatteryInternal = false;
         long uptime = SystemClock.uptimeMillis() * 1000;
         long realtime = SystemClock.elapsedRealtime() * 1000;
@@ -5930,6 +5968,7 @@
         mPhoneOnTimer.reset(false);
         mAudioOnTimer.reset(false);
         mVideoOnTimer.reset(false);
+        mFlashlightOnTimer.reset(false);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].reset(false);
         }
@@ -7124,6 +7163,8 @@
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i].readSummaryFromParcelLocked(in);
         }
+        mFlashlightOn = false;
+        mFlashlightOnTimer.readSummaryFromParcelLocked(in);
 
         int NKW = in.readInt();
         if (NKW > 10000) {
@@ -7381,6 +7422,7 @@
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
+        mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
 
         out.writeInt(mKernelWakelockStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -7667,6 +7709,8 @@
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOn = false;
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
+        mFlashlightOn = false;
+        mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
         mDischargeUnplugLevel = in.readInt();
         mDischargePlugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
@@ -7798,6 +7842,7 @@
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime);
         }
+        mFlashlightOnTimer.writeToParcel(out, uSecRealtime);
         out.writeInt(mDischargeUnplugLevel);
         out.writeInt(mDischargePlugLevel);
         out.writeInt(mDischargeCurrentLevel);
@@ -7931,6 +7976,8 @@
                 pr.println("*** Bluetooth active type #" + i + ":");
                 mBluetoothStateTimer[i].logState(pr, "  ");
             }
+            pr.println("*** Flashlight timer:");
+            mFlashlightOnTimer.logState(pr, "  ");
         }
         super.dumpLocked(context, pw, flags, reqUid, histStart);
     }
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 94750d3..b3bafa1 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -133,6 +133,11 @@
      */
     public static final String POWER_VIDEO = "dsp.video";
 
+    /**
+     * Power consumption when camera flashlight is on.
+     */
+    public static final String POWER_FLASHLIGHT = "camera.flashlight";
+
     public static final String POWER_CPU_SPEEDS = "cpu.speeds";
 
     /**
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 86c9fe3..50a7a5e 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -35,7 +35,7 @@
 
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
-            Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+            Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
index 30cd11b..8213cb7 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -133,11 +133,12 @@
         }
 
         final int contentInsetStart = a.getDimensionPixelOffset(
-                R.styleable.ActionBar_contentInsetStart, 0);
+                R.styleable.ActionBar_contentInsetStart, -1);
         final int contentInsetEnd = a.getDimensionPixelOffset(
-                R.styleable.ActionBar_contentInsetEnd, 0);
-        if (contentInsetStart > 0 || contentInsetEnd > 0) {
-            mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd);
+                R.styleable.ActionBar_contentInsetEnd, -1);
+        if (contentInsetStart >= 0 || contentInsetEnd >= 0) {
+            mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0),
+                    Math.max(contentInsetEnd, 0));
         }
 
         final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 4798f58..25d889e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -395,8 +395,8 @@
         property_get("ro.product.locale.language", propLang, "en");
         property_get("ro.product.locale.region", propRegn, "US");
     }
-    strncat(language, propLang, 2);
-    strncat(region, propRegn, 2);
+    strncat(language, propLang, 3);
+    strncat(region, propRegn, 3);
     //ALOGD("language=%s region=%s\n", language, region);
 }
 
@@ -494,6 +494,8 @@
     char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
     char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold:") + PROPERTY_VALUE_MAX];
     char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold:") + PROPERTY_VALUE_MAX];
+    char profile_type[sizeof("-Xprofile-type:") + PROPERTY_VALUE_MAX];
+    char profile_max_stack_depth[sizeof("-Xprofile-max-stack-depth:")+PROPERTY_VALUE_MAX];
     char langOption[sizeof("-Duser.language=") + 3];
     char regionOption[sizeof("-Duser.region=") + 3];
     char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
@@ -876,6 +878,20 @@
             opt.optionString = profile_top_k_change_threshold;
             mOptions.add(opt);
         }
+
+        // Type of profile data.
+        strcpy(profile_type, "-Xprofile-type:");
+        if (property_get("dalvik.vm.profiler.type", profile_type+15, NULL) > 0) {
+            opt.optionString = profile_type;
+            mOptions.add(opt);
+        }
+
+        // Depth of bounded stack data
+        strcpy(profile_max_stack_depth, "-Xprofile-max-stack-depth:");
+        if (property_get("dalvik.vm.profile.max-stack-depth", profile_max_stack_depth+26, NULL) > 0) {
+           opt.optionString = profile_max_stack_depth;
+           mOptions.add(opt);
+        }
     }
 
     initArgs.version = JNI_VERSION_1_4;
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c139c9d..f7886d3 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,6 +1,7 @@
 #include "SkBitmap.h"
 #include "SkPixelRef.h"
 #include "SkImageEncoder.h"
+#include "SkImageInfo.h"
 #include "SkColorPriv.h"
 #include "GraphicsJNI.h"
 #include "SkDither.h"
@@ -97,13 +98,14 @@
 }
 
 // can return NULL
-static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
-    switch (config) {
-        case SkBitmap::kARGB_8888_Config:
-            return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
-        case SkBitmap::kARGB_4444_Config:
-            return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
-        case SkBitmap::kRGB_565_Config:
+static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) {
+    switch (bitmap.colorType()) {
+        case kN32_SkColorType:
+            return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D32 : FromColor_D32_Raw;
+        case kARGB_4444_SkColorType:
+            return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D4444 :
+                    FromColor_D4444_Raw;
+        case kRGB_565_SkColorType:
             return FromColor_D565;
         default:
             break;
@@ -112,11 +114,10 @@
 }
 
 bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
-        int x, int y, int width, int height,
-        const SkBitmap& dstBitmap, bool isPremultiplied) {
+        int x, int y, int width, int height, const SkBitmap& dstBitmap) {
     SkAutoLockPixels alp(dstBitmap);
     void* dst = dstBitmap.getPixels();
-    FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
+    FromColorProc proc = ChooseFromColorProc(dstBitmap);
 
     if (NULL == dst || NULL == proc) {
         return false;
@@ -257,22 +258,46 @@
 }
 
 // can return NULL
-static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
+static ToColorProc ChooseToColorProc(const SkBitmap& src) {
     switch (src.colorType()) {
         case kN32_SkColorType:
-            if (src.isOpaque()) return ToColor_S32_Opaque;
-            return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
+            switch (src.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return ToColor_S32_Opaque;
+                case kPremul_SkAlphaType:
+                    return ToColor_S32_Alpha;
+                case kUnpremul_SkAlphaType:
+                    return ToColor_S32_Raw;
+                default:
+                    return NULL;
+            }
         case kARGB_4444_SkColorType:
-            if (src.isOpaque()) return ToColor_S4444_Opaque;
-            return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
+            switch (src.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return ToColor_S4444_Opaque;
+                case kPremul_SkAlphaType:
+                    return ToColor_S4444_Alpha;
+                case kUnpremul_SkAlphaType:
+                    return ToColor_S4444_Raw;
+                default:
+                    return NULL;
+            }
         case kRGB_565_SkColorType:
             return ToColor_S565;
         case kIndex_8_SkColorType:
             if (src.getColorTable() == NULL) {
                 return NULL;
             }
-            if (src.isOpaque()) return ToColor_SI8_Opaque;
-            return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
+            switch (src.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return ToColor_SI8_Opaque;
+                case kPremul_SkAlphaType:
+                    return ToColor_SI8_Alpha;
+                case kUnpremul_SkAlphaType:
+                    return ToColor_SI8_Raw;
+                default:
+                    return NULL;
+            }
         default:
             break;
     }
@@ -315,7 +340,7 @@
 
     if (jColors != NULL) {
         GraphicsJNI::SetPixels(env, jColors, offset, stride,
-                0, 0, width, height, bitmap, true);
+                0, 0, width, height, bitmap);
     }
 
     return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
@@ -477,20 +502,38 @@
     return static_cast<jint>(bitmap->getGenerationID());
 }
 
+static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    if (bitmap->alphaType() == kPremul_SkAlphaType) {
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
 }
 
-static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
-                                            jboolean hasAlpha, jboolean isPremul) {
+static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle,
+        jboolean hasAlpha, jboolean requestPremul) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    if (!hasAlpha) {
-        bitmap->setAlphaType(kOpaque_SkAlphaType);
-    } else if (isPremul) {
-        bitmap->setAlphaType(kPremul_SkAlphaType);
+    if (hasAlpha) {
+        bitmap->setAlphaType(requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
     } else {
-        bitmap->setAlphaType(kUnpremul_SkAlphaType);
+        bitmap->setAlphaType(kOpaque_SkAlphaType);
+    }
+}
+
+static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
+        jboolean isPremul) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    if (!bitmap->isOpaque()) {
+        if (isPremul) {
+            bitmap->setAlphaType(kPremul_SkAlphaType);
+        } else {
+            bitmap->setAlphaType(kUnpremul_SkAlphaType);
+        }
     }
 }
 
@@ -661,11 +704,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
-        jint x, jint y, jboolean isPremultiplied) {
+        jint x, jint y) {
     const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     SkAutoLockPixels alp(*bitmap);
 
-    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+    ToColorProc proc = ChooseToColorProc(*bitmap);
     if (NULL == proc) {
         return 0;
     }
@@ -681,11 +724,11 @@
 
 static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
         jintArray pixelArray, jint offset, jint stride,
-        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+        jint x, jint y, jint width, jint height) {
     const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     SkAutoLockPixels alp(*bitmap);
 
-    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+    ToColorProc proc = ChooseToColorProc(*bitmap);
     if (NULL == proc) {
         return;
     }
@@ -708,7 +751,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
-        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
+        jint x, jint y, jint colorHandle) {
     const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     SkColor color = static_cast<SkColor>(colorHandle);
     SkAutoLockPixels alp(*bitmap);
@@ -716,7 +759,7 @@
         return;
     }
 
-    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
+    FromColorProc proc = ChooseFromColorProc(*bitmap);
     if (NULL == proc) {
         return;
     }
@@ -727,10 +770,10 @@
 
 static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
         jintArray pixelArray, jint offset, jint stride,
-        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+        jint x, jint y, jint width, jint height) {
     const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
-            x, y, width, height, *bitmap, isPremultiplied);
+            x, y, width, height, *bitmap);
 }
 
 static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
@@ -834,7 +877,9 @@
     {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
     {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
     {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
-    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
+    {   "nativeIsPremultiplied",    "(J)Z", (void*)Bitmap_isPremultiplied},
+    {   "nativeSetHasAlpha",        "(JZZ)V", (void*)Bitmap_setHasAlpha},
+    {   "nativeSetPremultiplied",   "(JZ)V", (void*)Bitmap_setPremultiplied},
     {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
     {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
     {   "nativeCreateFromParcel",
@@ -845,10 +890,10 @@
     {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
         (void*)Bitmap_extractAlpha },
     {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
-    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },
-    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
-    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },
-    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
+    {   "nativeGetPixel",           "(JII)I", (void*)Bitmap_getPixel },
+    {   "nativeGetPixels",          "(J[IIIIIII)V", (void*)Bitmap_getPixels },
+    {   "nativeSetPixel",           "(JIII)V", (void*)Bitmap_setPixel },
+    {   "nativeSetPixels",          "(J[IIIIIII)V", (void*)Bitmap_setPixels },
     {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
                                             (void*)Bitmap_copyPixelsToBuffer },
     {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 3f323ab..6254f3d 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -24,6 +24,7 @@
 #include "SkDeque.h"
 #include "SkDrawFilter.h"
 #include "SkGraphics.h"
+#include <SkImageInfo.h>
 #include "SkPorterDuff.h"
 #include "SkShader.h"
 #include "SkTArray.h"
@@ -699,6 +700,8 @@
                                 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);
@@ -708,7 +711,7 @@
         }
 
         if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
-                0, 0, width, height, bitmap, true)) {
+                0, 0, width, height, bitmap)) {
             return;
         }
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7fda3d9..0ad7fd8 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -90,10 +90,11 @@
 
     /** Copy the colors in colors[] to the bitmap, convert to the correct
         format along the way.
+        Whether to use premultiplied pixels is determined by dstBitmap's alphaType.
     */
     static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset,
             int srcStride, int x, int y, int width, int height,
-            const SkBitmap& dstBitmap, bool isPremultiplied);
+            const SkBitmap& dstBitmap);
 
     static jbyteArray getBitmapStorageObj(SkPixelRef *pixref);
 };
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 2f24a69..9621bb2 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -25,10 +25,15 @@
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
 
+#include <gui/Surface.h>
+#include <gui/IGraphicBufferProducer.h>
 #include <ui/GraphicBuffer.h>
 #include <system/window.h>
 #include <hardware/camera3.h>
 
+#include <stdint.h>
+#include <inttypes.h>
+
 using namespace android;
 
 // fully-qualified class name
@@ -192,8 +197,8 @@
     switch(pixelFmt) {
         case HAL_PIXEL_FORMAT_YCrCb_420_SP: {
             if (bufSize < width * height * 4) {
-                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
-                        bufSize);
+                ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+                        __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
             uint8_t* img = NULL;
@@ -214,8 +219,8 @@
         }
         case HAL_PIXEL_FORMAT_YV12: {
             if (bufSize < width * height * 4) {
-                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
-                        bufSize);
+                ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+                        __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
 
@@ -251,8 +256,8 @@
             // Software writes with YCbCr_420_888 format are unsupported
             // by the gralloc module for now
             if (bufSize < width * height * 4) {
-                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
-                      bufSize);
+                ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+                        __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
             android_ycbcr ycbcr = android_ycbcr();
@@ -269,7 +274,7 @@
         }
         case HAL_PIXEL_FORMAT_BLOB: {
             if (bufSize != width || height != 1) {
-                ALOGE("%s: Incorrect pixelBuffer size: %lld", __FUNCTION__, bufSize);
+                ALOGE("%s: Incorrect pixelBuffer size: %" PRId32, __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
             int8_t* img = NULL;
@@ -316,20 +321,39 @@
     if (surface) {
         anw = android_view_Surface_getNativeWindow(env, surface);
         if (env->ExceptionCheck()) {
-            return anw;
+            return NULL;
         }
     } else {
         jniThrowNullPointerException(env, "surface");
-        return anw;
+        return NULL;
     }
     if (anw == NULL) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                 "Surface had no valid native window.");
-        return anw;
+        return NULL;
     }
     return anw;
 }
 
+static sp<Surface> getSurface(JNIEnv* env, jobject surface) {
+    sp<Surface> s;
+    if (surface) {
+        s = android_view_Surface_getSurface(env, surface);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+    } else {
+        jniThrowNullPointerException(env, "surface");
+        return NULL;
+    }
+    if (s == NULL) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "Surface had no valid native Surface.");
+        return NULL;
+    }
+    return s;
+}
+
 extern "C" {
 
 static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) {
@@ -456,6 +480,30 @@
     return NO_ERROR;
 }
 
+static jlong LegacyCameraDevice_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jobject surface) {
+    ALOGV("nativeGetSurfaceId");
+    sp<Surface> s;
+    if ((s = getSurface(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native Surface from surface.", __FUNCTION__);
+        return 0;
+    }
+    sp<IGraphicBufferProducer> gbp = s->getIGraphicBufferProducer();
+    if (gbp == NULL) {
+        ALOGE("%s: Could not retrieve IGraphicBufferProducer from surface.", __FUNCTION__);
+        return 0;
+    }
+    sp<IBinder> b = gbp->asBinder();
+    if (b == NULL) {
+        ALOGE("%s: Could not retrieve IBinder from surface.", __FUNCTION__);
+        return 0;
+    }
+    /*
+     * FIXME: Use better unique ID for surfaces than native IBinder pointer.  Fix also in the camera
+     * service (CameraDeviceClient.h).
+     */
+    return reinterpret_cast<jlong>(b.get());
+}
+
 } // extern "C"
 
 static JNINativeMethod gCameraDeviceMethods[] = {
@@ -477,6 +525,9 @@
     { "nativeSetSurfaceDimens",
     "(Landroid/view/Surface;II)I",
     (void *)LegacyCameraDevice_nativeSetSurfaceDimens },
+    { "nativeGetSurfaceId",
+    "(Landroid/view/Surface;)J",
+    (void *)LegacyCameraDevice_nativeGetSurfaceId },
 };
 
 // Get all the required offsets in java class and register native functions
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b38f5f2..c352398 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -30,6 +30,7 @@
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
+#include <SkImageInfo.h>
 #include <SkMatrix.h>
 #include <SkPaint.h>
 #include <SkPorterDuff.h>
@@ -376,6 +377,8 @@
 static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jintArray colors, jint offset, jint stride,
         jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
+    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+    // correct the alphaType to kOpaque_SkAlphaType.
     const SkImageInfo info = SkImageInfo::Make(width, height,
                                hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
                                kPremul_SkAlphaType);
@@ -385,7 +388,7 @@
         return;
     }
 
-    if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap, true)) {
+    if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap)) {
         delete bitmap;
         return;
     }
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 6ba22bf..28473e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -456,7 +456,6 @@
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
     renderNode->addAnimator(animator);
-    animator->start();
 }
 
 #endif // USE_OPENGL_RENDERER
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index de3dd16..ed57979 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -149,6 +149,11 @@
     animator->setInterpolator(interpolator);
 }
 
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    animator->start();
+}
+
 static void cancel(JNIEnv* env, jobject clazz, jlong animatorPtr) {
     BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
     animator->cancel();
@@ -173,6 +178,7 @@
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
     { "nGetStartDelay", "(J)J", (void*) getStartDelay },
     { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+    { "nStart", "(J)V", (void*) start },
     { "nCancel", "(J)V", (void*) cancel },
 #endif
 };
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7db478d..e675dc7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -99,6 +99,7 @@
     <protected-broadcast android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.device.action.UUID" />
+    <protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
     <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
     <protected-broadcast android:name="android.bluetooth.device.action.DISAPPEARED" />
@@ -253,6 +254,7 @@
     <protected-broadcast
             android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
     <protected-broadcast android:name="android.net.scoring.SCORE_NETWORKS" />
+    <protected-broadcast android:name="android.net.scoring.SCORER_CHANGED" />
     <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
     <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" />
     <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
@@ -396,6 +398,15 @@
         android:label="@string/permlab_receiveWapPush"
         android:description="@string/permdesc_receiveWapPush" />
 
+    <!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
+         or perform processing on them. -->
+    <!-- @hide -->
+    <permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
+        android:permissionGroup="android.permission-group.MESSAGES"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_receiveBluetoothMap"
+        android:description="@string/permdesc_receiveBluetoothMap" />
+
     <!-- =============================================================== -->
     <!-- Permissions for accessing social info (contacts and social) -->
     <!-- =============================================================== -->
diff --git a/core/res/res/anim/button_state_list_anim_material.xml b/core/res/res/anim/button_state_list_anim_material.xml
index 01989a4..9a61548 100644
--- a/core/res/res/anim/button_state_list_anim_material.xml
+++ b/core/res/res/anim/button_state_list_anim_material.xml
@@ -18,16 +18,37 @@
         <set>
             <objectAnimator android:propertyName="translationZ"
                             android:duration="@integer/button_pressed_animation_duration"
-                            android:valueTo="@dimen/button_pressed_z"
+                            android:valueTo="@dimen/button_pressed_z_material"
+                            android:valueType="floatType"/>
+            <objectAnimator android:propertyName="elevation"
+                            android:duration="0"
+                            android:valueTo="@dimen/button_elevation_material"
                             android:valueType="floatType"/>
         </set>
     </item>
     <!-- base state -->
-    <item>
+    <item android:state_enabled="true">
         <set>
             <objectAnimator android:propertyName="translationZ"
                             android:duration="@integer/button_pressed_animation_duration"
                             android:valueTo="0"
+                            android:startDelay="@integer/button_pressed_animation_delay"
+                            android:valueType="floatType"/>
+            <objectAnimator android:propertyName="elevation"
+                            android:duration="0"
+                            android:valueTo="@dimen/button_elevation_material"
+                            android:valueType="floatType" />
+        </set>
+    </item>
+    <item>
+        <set>
+            <objectAnimator android:propertyName="translationZ"
+                            android:duration="0"
+                            android:valueTo="0"
+                            android:valueType="floatType"/>
+            <objectAnimator android:propertyName="elevation"
+                            android:duration="0"
+                            android:valueTo="0"
                             android:valueType="floatType"/>
         </set>
     </item>
diff --git a/core/res/res/anim/progress_indeterminate_rotation_material.xml b/core/res/res/anim/progress_indeterminate_rotation_material.xml
new file mode 100644
index 0000000..5d3ba22
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_rotation_material.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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="6665"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:propertyName="rotation"
+    android:repeatCount="-1"
+    android:valueFrom="0"
+    android:valueTo="720"
+    android:valueType="floatType" />
\ No newline at end of file
diff --git a/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 171688f..0000000
--- a/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index 879d9c2..0000000
--- a/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 1b648db..0000000
--- a/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index fc51595..0000000
--- a/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index dca86ea..0000000
--- a/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 30d732a..0000000
--- a/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index b135338..0000000
--- a/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 7d29d18..0000000
--- a/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index f235aed..0000000
--- a/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 01eeefe..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100755
index 7556167..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
index a459089..47cc455 100644
--- a/core/res/res/drawable/btn_borderless_material.xml
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -14,8 +14,13 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?attr/colorControlHighlight">
-    <item android:id="@id/mask"
-        android:drawable="@drawable/btn_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/colorControlHighlight">
+        <item android:id="@id/mask"
+              android:drawable="@drawable/btn_default_mtrl_shape" />
+    </ripple>
+</inset>
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
index 9cee3ab..b04d4fb 100644
--- a/core/res/res/drawable/btn_default_material.xml
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -14,10 +14,12 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?attr/colorControlHighlight">
-    <item>
-        <nine-patch android:src="@drawable/btn_mtrl_alpha"
-            android:tint="?attr/colorButtonNormal" />
-    </item>
-</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/colorControlHighlight">
+        <item android:drawable="@drawable/btn_default_mtrl_shape" />
+    </ripple>
+</inset>
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
new file mode 100644
index 0000000..9235c76
--- /dev/null
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<!-- Used as the canonical button shape. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:radius="@dimen/control_corner_material" />
+    <solid android:color="?attr/colorButtonNormal" />
+    <padding android:top="@dimen/control_padding_material"
+             android:bottom="@dimen/control_padding_material"
+             android:left="@dimen/control_padding_material"
+             android:right="@dimen/control_padding_material" />
+</shape>
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
index 73fe4d3..a9951e7 100644
--- a/core/res/res/drawable/btn_toggle_material.xml
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -15,16 +15,24 @@
 -->
 
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetLeft="4dp"
-    android:insetTop="4dp"
-    android:insetBottom="4dp"
-    android:insetRight="4dp">
+       android:insetLeft="@dimen/control_inset_material"
+       android:insetTop="@dimen/control_inset_material"
+       android:insetBottom="@dimen/control_inset_material"
+       android:insetRight="@dimen/control_inset_material">
     <layer-list android:paddingMode="stack">
         <item>
             <ripple android:color="?attr/colorControlHighlight">
                 <item>
-                    <nine-patch android:src="@drawable/btn_toggle_mtrl_alpha"
-                        android:tint="?attr/colorButtonNormal" />
+                    <shape xmlns:android="http://schemas.android.com/apk/res/android"
+                           android:shape="rectangle">
+                        <corners android:topLeftRadius="@dimen/control_corner_material"
+                                 android:topRightRadius="@dimen/control_corner_material"/>
+                        <solid android:color="?attr/colorButtonNormal" />
+                        <padding android:top="@dimen/control_padding_material"
+                                 android:bottom="@dimen/control_padding_material"
+                                 android:left="@dimen/control_padding_material"
+                                 android:right="@dimen/control_padding_material" />
+                    </shape>
                 </item>
             </ripple>
         </item>
diff --git a/core/res/res/drawable/progress_large_material.xml b/core/res/res/drawable/progress_large_material.xml
index 341d2a9..526f914 100644
--- a/core/res/res/drawable/progress_large_material.xml
+++ b/core/res/res/drawable/progress_large_material.xml
@@ -21,4 +21,8 @@
         android:name="progressBar"
         android:animation="@anim/progress_indeterminate_material" />
 
+    <target
+        android:name="root"
+        android:animation="@anim/progress_indeterminate_rotation_material" />
+
 </animated-vector>
diff --git a/core/res/res/drawable/progress_medium_material.xml b/core/res/res/drawable/progress_medium_material.xml
index 1f2622c..cc35816 100644
--- a/core/res/res/drawable/progress_medium_material.xml
+++ b/core/res/res/drawable/progress_medium_material.xml
@@ -20,4 +20,8 @@
         android:name="progressBar"
         android:animation="@anim/progress_indeterminate_material" />
 
+    <target
+        android:name="root"
+        android:animation="@anim/progress_indeterminate_rotation_material" />
+
 </animated-vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_small_material.xml b/core/res/res/drawable/progress_small_material.xml
index 43bc569..c6e4380 100644
--- a/core/res/res/drawable/progress_small_material.xml
+++ b/core/res/res/drawable/progress_small_material.xml
@@ -21,4 +21,8 @@
         android:name="progressBar"
         android:animation="@anim/progress_indeterminate_material" />
 
+    <target
+        android:name="root"
+        android:animation="@anim/progress_indeterminate_rotation_material" />
+
 </animated-vector>
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
index b1900cb..6e0840c 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
@@ -25,8 +25,6 @@
 
     <group
         android:name="root"
-        android:scaleX="1.0"
-        android:scaleY="1.0"
         android:translateX="24.0"
         android:translateY="24.0" >
         <path
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
index 57a6163..7f1231c 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
@@ -25,8 +25,6 @@
 
     <group
         android:name="root"
-        android:scaleX="1.0"
-        android:scaleY="1.0"
         android:translateX="24.0"
         android:translateY="24.0" >
         <path
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
index ea949b7..58ca101 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
@@ -25,8 +25,6 @@
 
     <group
         android:name="root"
-        android:scaleX="1.0"
-        android:scaleY="1.0"
         android:translateX="24.0"
         android:translateY="24.0" >
         <path
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index b135d4e..11d6dca 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -335,7 +335,6 @@
        <item>@drawable/btn_check_to_on_mtrl_013</item>
        <item>@drawable/btn_check_to_on_mtrl_014</item>
        <item>@drawable/btn_check_to_on_mtrl_015</item>
-       <item>@drawable/btn_mtrl_alpha</item>
        <item>@drawable/btn_radio_to_off_mtrl_000</item>
        <item>@drawable/btn_radio_to_off_mtrl_001</item>
        <item>@drawable/btn_radio_to_off_mtrl_002</item>
@@ -402,7 +401,6 @@
        <item>@drawable/btn_switch_to_on_mtrl_013</item>
        <item>@drawable/btn_switch_to_on_mtrl_014</item>
        <item>@drawable/btn_toggle_indicator_mtrl_alpha</item>
-       <item>@drawable/btn_toggle_mtrl_alpha</item>
        <item>@drawable/expander_close_mtrl_alpha</item>
        <item>@drawable/expander_open_mtrl_alpha</item>
        <item>@drawable/fastscroll_thumb_mtrl_alpha</item>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 18e4574..bae8e8d 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -58,6 +58,14 @@
     <dimen name="floating_window_margin_right">16dp</dimen>
     <dimen name="floating_window_margin_bottom">32dp</dimen>
 
-    <!-- Amount of elevation for pressed button state -->
-    <dimen name="button_pressed_z">2dp</dimen>
+    <!-- Elevation when button is pressed -->
+    <dimen name="button_elevation_material">1dp</dimen>
+    <!-- Z translation to apply when button is pressed -->
+    <dimen name="button_pressed_z_material">2dp</dimen>
+    <!-- Default insets (outer padding) around controls -->
+    <dimen name="control_inset_material">4dp</dimen>
+    <!-- Default inner padding within controls -->
+    <dimen name="control_padding_material">4dp</dimen>
+    <!-- Default rounded corner for controls -->
+    <dimen name="control_corner_material">2dp</dimen>
 </resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index e6748c4..fd61f73 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -20,4 +20,5 @@
     <integer name="kg_carousel_angle">75</integer>
     <integer name="kg_glowpad_rotation_offset">0</integer>
     <integer name="button_pressed_animation_duration">100</integer>
+    <integer name="button_pressed_animation_delay">100</integer>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a3262ed..a71ed47 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -761,6 +761,13 @@
      messages sent to you without showing them to you.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_receiveBluetoothMap">receive Bluetooth messages (MAP)</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_receiveBluetoothMap">Allows the app to receive and process Bluetooth MAP
+      messages. This means the app could monitor or delete messages sent to your
+      device without showing them to you.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_getTasks">retrieve running apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_getTasks">Allows the app to retrieve information
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 75f905c..b496ead 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -355,35 +355,35 @@
     <style name="TextAppearance.StatusBar.Material" />
 
     <style name="TextAppearance.StatusBar.Material.EventContent">
-        <item name="android:textColor">#90000000</item>
-        <item name="android:textSize">@dimen/notification_text_size</item>
+        <item name="textColor">#90000000</item>
+        <item name="textSize">@dimen/notification_text_size</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Material.EventContent.Title">
-        <item name="android:textColor">#DD000000</item>
-        <item name="android:textSize">@dimen/notification_title_text_size</item>
+        <item name="textColor">#DD000000</item>
+        <item name="textSize">@dimen/notification_title_text_size</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Material.EventContent.Line2">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
+        <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Material.EventContent.Info">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
+        <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Material.EventContent.Time">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
+        <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Material.EventContent.Emphasis">
-        <item name="android:textColor">#66000000</item>
+        <item name="textColor">#66000000</item>
     </style>
 
     <style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
 
     <style name="Widget.StatusBar.Material.ProgressBar.Media">
-        <item name="android:progressDrawable">@drawable/notification_material_media_progress</item>
+        <item name="progressDrawable">@drawable/notification_material_media_progress</item>
     </style>
 
     <!-- Widget Styles -->
@@ -399,9 +399,7 @@
         <item name="textColor">?attr/textColorPrimary</item>
         <item name="minHeight">48dip</item>
         <item name="minWidth">88dip</item>
-
-        <!-- TODO: Turn this back on when we support inset drawable outlines. -->
-        <!-- <item name="stateListAnimator">@anim/button_state_list_anim_material</item> -->
+        <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
     </style>
 
     <!-- Small bordered ink button -->
@@ -430,7 +428,6 @@
         <item name="background">@drawable/btn_toggle_material</item>
         <item name="textOn">@string/capital_on</item>
         <item name="textOff">@string/capital_off</item>
-        <item name="minHeight">48dip</item>
     </style>
 
     <style name="Widget.Material.ButtonBar">
@@ -515,7 +512,7 @@
     <style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
 
     <style name="Widget.Material.GridView" parent="Widget.GridView">
-        <item name="android:listSelector">?attr/selectableItemBackground</item>
+        <item name="listSelector">?attr/selectableItemBackground</item>
     </style>
 
     <style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
@@ -687,11 +684,11 @@
     </style>
 
     <style name="Widget.Material.Toolbar" parent="Widget.Toolbar">
-        <item name="android:navigationButtonStyle">@android:style/Widget.Material.Toolbar.Button.Navigation</item>
+        <item name="navigationButtonStyle">@android:style/Widget.Material.Toolbar.Button.Navigation</item>
     </style>
 
     <style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Toolbar.Button.Navigation">
-        <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+        <item name="background">?attr/selectableItemBackgroundBorderless</item>
     </style>
 
     <style name="Widget.Material.WebTextView" parent="Widget.WebTextView"/>
diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
index 9252270..33192f2 100644
--- a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
+++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
@@ -59,9 +59,6 @@
         final float INSERTION_MARKER_TOP = 100.1f;
         final float INSERTION_MARKER_BASELINE = 110.4f;
         final float INSERTION_MARKER_BOTOM = 111.0f;
-        final int CHAR_INDEX = 32;
-        final char CHAR_VALUE = 'X';
-        final char DEFAULT_CHAR_VALUE = '!';
         Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
         TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
 
@@ -138,6 +135,7 @@
         assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop());
         assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline());
         assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom());
+        assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
     }
 
     @SmallTest
@@ -169,6 +167,54 @@
     }
 
     @SmallTest
+    public void testMatrixIsRequired() throws Exception {
+        final int SELECTION_START = 30;
+        final int SELECTION_END = 40;
+        final int COMPOSING_TEXT_START = 32;
+        final String COMPOSING_TEXT = "test";
+        final float INSERTION_MARKER_HORIZONTAL = 10.5f;
+        final float INSERTION_MARKER_TOP = 100.1f;
+        final float INSERTION_MARKER_BASELINE = 110.4f;
+        final float INSERTION_MARKER_BOTOM = 111.0f;
+        Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
+        TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
+
+        final Builder builder = new Builder();
+        // Check twice to make sure if Builder#reset() works as expected.
+        for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
+            builder.setSelectionRange(SELECTION_START, SELECTION_END)
+                    .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT);
+            try {
+                // Should succeed as coordinate transformation matrix is not required if no
+                // positional information is specified.
+                new Builder().build();
+            } catch (IllegalArgumentException ex) {
+                assertTrue(false);
+            }
+
+            builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
+                    INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM);
+            try {
+                // Coordinate transformation matrix is required if no positional information is
+                // specified.
+                new Builder().build();
+            } catch (IllegalArgumentException ex) {
+                assertTrue(true);
+            }
+
+            builder.setMatrix(TRANSFORM_MATRIX);
+            try {
+                // Should succeed as coordinate transformation matrix is required.
+                new Builder().build();
+            } catch (IllegalArgumentException ex) {
+                assertTrue(false);
+            }
+
+            builder.reset();
+        }
+    }
+
+    @SmallTest
     public void testBuilderAdd() throws Exception {
         // A negative index should be rejected.
         try {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 3857ec0..91f5896 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -141,4 +141,8 @@
     <library name="javax.obex"
             file="/system/framework/javax.obex.jar"/>
 
+    <!-- These are the standard packages that are white-listed to always have internet
+         access while in power save mode, even if they aren't in the foreground. -->
+    <allow-in-power-save package="com.android.providers.downloads" />
+
 </permissions>
diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf
index cb905bc..1d15caa 100644
--- a/data/fonts/Roboto-Black.ttf
+++ b/data/fonts/Roboto-Black.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BlackItalic.ttf b/data/fonts/Roboto-BlackItalic.ttf
index 3ebdc7d..ea35b0b 100644
--- a/data/fonts/Roboto-BlackItalic.ttf
+++ b/data/fonts/Roboto-BlackItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 68822ca..de790fd 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index aebf8eb..49a41d6 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 2041cbc..345febe 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index aa45340..559eaef 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index a85444f..00ed18d 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
index a3c1a1f..7787a51 100644
--- a/data/fonts/Roboto-Medium.ttf
+++ b/data/fonts/Roboto-Medium.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
index b828205..de787a7 100644
--- a/data/fonts/Roboto-MediumItalic.ttf
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 0e58508..d435eca 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 8779333..164b33d 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index b79cb26..7e00da0 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 3e06c7c..901c708 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index aaf9fe0..3ecb90b 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index d2b611f..986f81c 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf
index d4eb198..6e4e2ea 100644
--- a/data/fonts/RobotoCondensed-Light.ttf
+++ b/data/fonts/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf
index a08f3f4..29ae84b 100644
--- a/data/fonts/RobotoCondensed-LightItalic.ttf
+++ b/data/fonts/RobotoCondensed-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index b9fc49c..4298dea 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/docs/html/preview/images/notifications/ReplyAction.png b/docs/html/preview/images/notifications/ReplyAction.png
new file mode 100644
index 0000000..78fc048
--- /dev/null
+++ b/docs/html/preview/images/notifications/ReplyAction.png
Binary files differ
diff --git a/docs/html/preview/images/notifications/Stack.png b/docs/html/preview/images/notifications/Stack.png
new file mode 100644
index 0000000..c6d76b0
--- /dev/null
+++ b/docs/html/preview/images/notifications/Stack.png
Binary files differ
diff --git a/docs/html/preview/images/notifications/WearBasic.png b/docs/html/preview/images/notifications/WearBasic.png
new file mode 100644
index 0000000..8482cd6
--- /dev/null
+++ b/docs/html/preview/images/notifications/WearBasic.png
Binary files differ
diff --git a/docs/html/preview/images/notifications/expandedtext_combo.png b/docs/html/preview/images/notifications/expandedtext_combo.png
index 1a54033..1bc276f 100644
--- a/docs/html/preview/images/notifications/expandedtext_combo.png
+++ b/docs/html/preview/images/notifications/expandedtext_combo.png
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index ef0a411..9d7ef45 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -59,14 +59,18 @@
     private final boolean mIsMutable;
 
     /**
-     * Represents whether the Bitmap's content is expected to be pre-multiplied.
+     * Represents whether the Bitmap's content is requested to be pre-multiplied.
      * Note that isPremultiplied() does not directly return this value, because
-     * isPremultiplied() may never return true for a 565 Bitmap.
+     * isPremultiplied() may never return true for a 565 Bitmap or a bitmap
+     * without alpha.
      *
      * setPremultiplied() does directly set the value so that setConfig() and
      * setPremultiplied() aren't order dependent, despite being setters.
+     *
+     * The native bitmap's premultiplication state is kept up to date by
+     * pushing down this preference for every config change.
      */
-    private boolean mIsPremultiplied;
+    private boolean mRequestPremultiplied;
 
     private byte[] mNinePatchChunk;   // may be null
     private int[] mLayoutBounds;   // may be null
@@ -105,7 +109,7 @@
      */
     @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
     Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
-            boolean isMutable, boolean isPremultiplied,
+            boolean isMutable, boolean requestPremultiplied,
             byte[] ninePatchChunk, int[] layoutBounds) {
         if (nativeBitmap == 0) {
             throw new RuntimeException("internal error: native bitmap is 0");
@@ -114,7 +118,7 @@
         mWidth = width;
         mHeight = height;
         mIsMutable = isMutable;
-        mIsPremultiplied = isPremultiplied;
+        mRequestPremultiplied = requestPremultiplied;
         mBuffer = buffer;
         // we delete this in our finalizer
         mNativeBitmap = nativeBitmap;
@@ -132,10 +136,10 @@
      * width/height values
      */
     @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
-    void reinit(int width, int height, boolean isPremultiplied) {
+    void reinit(int width, int height, boolean requestPremultiplied) {
         mWidth = width;
         mHeight = height;
-        mIsPremultiplied = isPremultiplied;
+        mRequestPremultiplied = requestPremultiplied;
     }
 
     /**
@@ -223,7 +227,7 @@
         }
 
         nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length,
-                mIsPremultiplied);
+                mRequestPremultiplied);
         mWidth = width;
         mHeight = height;
     }
@@ -561,7 +565,7 @@
         checkRecycled("Can't copy a recycled bitmap");
         Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable);
         if (b != null) {
-            b.setAlphaAndPremultiplied(hasAlpha(), mIsPremultiplied);
+            b.setPremultiplied(mRequestPremultiplied);
             b.mDensity = mDensity;
         }
         return b;
@@ -738,7 +742,8 @@
         // The new bitmap was created from a known bitmap source so assume that
         // they use the same density
         bitmap.mDensity = source.mDensity;
-        bitmap.setAlphaAndPremultiplied(source.hasAlpha(), source.mIsPremultiplied);
+        bitmap.setHasAlpha(source.hasAlpha());
+        bitmap.setPremultiplied(source.mRequestPremultiplied);
 
         canvas.setBitmap(bitmap);
         canvas.drawBitmap(source, srcR, dstR, paint);
@@ -1018,6 +1023,9 @@
      * <p>This method always returns false if {@link #getConfig()} is
      * {@link Bitmap.Config#RGB_565}.</p>
      * 
+     * <p>The return value is undefined if {@link #getConfig()} is
+     * {@link Bitmap.Config#ALPHA_8}.</p>
+     *
      * <p>This method only returns true if {@link #hasAlpha()} returns true.
      * A bitmap with no alpha channel can be used both as a pre-multiplied and
      * as a non pre-multiplied bitmap.</p>
@@ -1033,7 +1041,7 @@
      * @see BitmapFactory.Options#inPremultiplied
      */
     public final boolean isPremultiplied() {
-        return mIsPremultiplied && getConfig() != Config.RGB_565 && hasAlpha();
+        return nativeIsPremultiplied(mNativeBitmap);
     }
 
     /**
@@ -1057,14 +1065,8 @@
      * @see BitmapFactory.Options#inPremultiplied
      */
     public final void setPremultiplied(boolean premultiplied) {
-        mIsPremultiplied = premultiplied;
-        nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha(), premultiplied);
-    }
-
-    /** Helper function to set both alpha and premultiplied. **/
-    private final void setAlphaAndPremultiplied(boolean hasAlpha, boolean premultiplied) {
-        mIsPremultiplied = premultiplied;
-        nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, premultiplied);
+        mRequestPremultiplied = premultiplied;
+        nativeSetPremultiplied(mNativeBitmap, premultiplied);
     }
 
     /** Returns the bitmap's width */
@@ -1225,7 +1227,7 @@
      * non-opaque per-pixel alpha values.
      */
     public void setHasAlpha(boolean hasAlpha) {
-        nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, mIsPremultiplied);
+        nativeSetHasAlpha(mNativeBitmap, hasAlpha, mRequestPremultiplied);
     }
 
     /**
@@ -1299,7 +1301,7 @@
     public int getPixel(int x, int y) {
         checkRecycled("Can't call getPixel() on a recycled bitmap");
         checkPixelAccess(x, y);
-        return nativeGetPixel(mNativeBitmap, x, y, mIsPremultiplied);
+        return nativeGetPixel(mNativeBitmap, x, y);
     }
 
     /**
@@ -1333,7 +1335,7 @@
         }
         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
         nativeGetPixels(mNativeBitmap, pixels, offset, stride,
-                        x, y, width, height, mIsPremultiplied);
+                        x, y, width, height);
     }
 
     /**
@@ -1413,7 +1415,7 @@
             throw new IllegalStateException();
         }
         checkPixelAccess(x, y);
-        nativeSetPixel(mNativeBitmap, x, y, color, mIsPremultiplied);
+        nativeSetPixel(mNativeBitmap, x, y, color);
     }
 
     /**
@@ -1450,7 +1452,7 @@
         }
         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
         nativeSetPixels(mNativeBitmap, pixels, offset, stride,
-                        x, y, width, height, mIsPremultiplied);
+                        x, y, width, height);
     }
 
     public static final Parcelable.Creator<Bitmap> CREATOR
@@ -1602,17 +1604,15 @@
     private static native int nativeRowBytes(long nativeBitmap);
     private static native int nativeConfig(long nativeBitmap);
 
-    private static native int nativeGetPixel(long nativeBitmap, int x, int y,
-                                             boolean isPremultiplied);
+    private static native int nativeGetPixel(long nativeBitmap, int x, int y);
     private static native void nativeGetPixels(long nativeBitmap, int[] pixels,
                                                int offset, int stride, int x, int y,
-                                               int width, int height, boolean isPremultiplied);
+                                               int width, int height);
 
-    private static native void nativeSetPixel(long nativeBitmap, int x, int y,
-                                              int color, boolean isPremultiplied);
+    private static native void nativeSetPixel(long nativeBitmap, int x, int y, int color);
     private static native void nativeSetPixels(long nativeBitmap, int[] colors,
                                                int offset, int stride, int x, int y,
-                                               int width, int height, boolean isPremultiplied);
+                                               int width, int height);
     private static native void nativeCopyPixelsToBuffer(long nativeBitmap,
                                                         Buffer dst);
     private static native void nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src);
@@ -1631,9 +1631,12 @@
 
     private static native void nativePrepareToDraw(long nativeBitmap);
     private static native boolean nativeHasAlpha(long nativeBitmap);
-    private static native void nativeSetAlphaAndPremultiplied(long nativeBitmap,
-                                                              boolean hasAlpha,
-                                                              boolean isPremul);
+    private static native boolean nativeIsPremultiplied(long nativeBitmap);
+    private static native void nativeSetPremultiplied(long nativeBitmap,
+                                                      boolean isPremul);
+    private static native void nativeSetHasAlpha(long nativeBitmap,
+                                                 boolean hasAlpha,
+                                                 boolean requestPremul);
     private static native boolean nativeHasMipMap(long nativeBitmap);
     private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
     private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 4a8c122..f3ef48b 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -32,7 +32,8 @@
  ************************************************************/
 
 BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
-        : mFinalValue(finalValue)
+        : mTarget(NULL)
+        , mFinalValue(finalValue)
         , mDeltaValue(0)
         , mFromValue(0)
         , mInterpolator(0)
@@ -81,9 +82,14 @@
     mStartDelay = startDelay;
 }
 
-void BaseRenderNodeAnimator::pushStaging(RenderNode* target, TreeInfo& info) {
+void BaseRenderNodeAnimator::attach(RenderNode* target) {
+    mTarget = target;
+    onAttached();
+}
+
+void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) {
     if (!mHasStartValue) {
-        doSetStartValue(getValue(target));
+        doSetStartValue(getValue(mTarget));
     }
     if (mStagingPlayState > mPlayState) {
         mPlayState = mStagingPlayState;
@@ -109,20 +115,25 @@
     }
     // No interpolator was set, use the default
     if (!mInterpolator) {
-        setInterpolator(Interpolator::createDefaultInterpolator());
+        mInterpolator = Interpolator::createDefaultInterpolator();
     }
     if (mDuration < 0 || mDuration > 50000) {
         ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
     }
 }
 
-bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
+bool BaseRenderNodeAnimator::animate(TreeInfo& info) {
     if (mPlayState < RUNNING) {
         return false;
     }
 
+    // If BaseRenderNodeAnimator is handling the delay (not typical), then
+    // because the staging properties reflect the final value, we always need
+    // to call setValue even if the animation isn't yet running or is still
+    // being delayed as we need to override the staging value
     if (mStartTime > info.frameTimeMs) {
         info.out.hasAnimations |= true;
+        setValue(mTarget, mFromValue);
         return false;
     }
 
@@ -136,7 +147,7 @@
     }
 
     fraction = mInterpolator->interpolate(fraction);
-    setValue(target, mFromValue + (mDeltaValue * fraction));
+    setValue(mTarget, mFromValue + (mDeltaValue * fraction));
 
     if (mPlayState == FINISHED) {
         callOnFinishedListener(info);
@@ -188,12 +199,17 @@
         , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {
 }
 
-void RenderPropertyAnimator::onAttached(RenderNode* target) {
+void RenderPropertyAnimator::onAttached() {
     if (!mHasStartValue
-            && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
-        setStartValue((target->stagingProperties().*mPropertyAccess->getter)());
+            && mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+        setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)());
     }
-    (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+}
+
+void RenderPropertyAnimator::onStagingPlayStateChanged() {
+    if (mStagingPlayState == RUNNING) {
+        (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+    }
 }
 
 uint32_t RenderPropertyAnimator::dirtyMask() {
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index a981b5a..0dda23f 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -50,12 +50,14 @@
     ANDROID_API void setListener(AnimationListener* listener) {
         mListener = listener;
     }
-    ANDROID_API void start() { mStagingPlayState = RUNNING; }
-    ANDROID_API void cancel() { mStagingPlayState = FINISHED; }
+    ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
+    ANDROID_API void cancel() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
 
-    virtual void onAttached(RenderNode* target) {}
-    virtual void pushStaging(RenderNode* target, TreeInfo& info);
-    bool animate(RenderNode* target, TreeInfo& info);
+    void attach(RenderNode* target);
+    virtual void onAttached() {}
+    void detach() { mTarget = 0; }
+    void pushStaging(TreeInfo& info);
+    bool animate(TreeInfo& info);
 
     bool isFinished() { return mPlayState == FINISHED; }
     float finalValue() { return mFinalValue; }
@@ -68,15 +70,20 @@
 
     virtual float getValue(RenderNode* target) const = 0;
     virtual void setValue(RenderNode* target, float value) = 0;
+    RenderNode* target() { return mTarget; }
 
     void callOnFinishedListener(TreeInfo& info);
 
+    virtual void onStagingPlayStateChanged() {}
+
     enum PlayState {
         NOT_STARTED,
         RUNNING,
         FINISHED,
     };
 
+    RenderNode* mTarget;
+
     float mFinalValue;
     float mDeltaValue;
     float mFromValue;
@@ -92,9 +99,9 @@
     sp<AnimationListener> mListener;
 
 private:
-    void doSetStartValue(float value);
     inline void checkMutable();
-    void transitionToRunning(TreeInfo& info);
+    virtual void transitionToRunning(TreeInfo& info);
+    void doSetStartValue(float value);
 };
 
 class RenderPropertyAnimator : public BaseRenderNodeAnimator {
@@ -116,13 +123,13 @@
 
     ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue);
 
-    virtual void onAttached(RenderNode* target);
-
     ANDROID_API virtual uint32_t dirtyMask();
 
 protected:
     virtual float getValue(RenderNode* target) const;
     virtual void setValue(RenderNode* target, float value);
+    virtual void onAttached();
+    virtual void onStagingPlayStateChanged();
 
 private:
     typedef bool (RenderProperties::*SetFloatProperty)(float value);
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 6a10cf8..27b0893 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -25,6 +25,7 @@
 using namespace std;
 
 static void unref(BaseRenderNodeAnimator* animator) {
+    animator->detach();
     animator->decStrong(0);
 }
 
@@ -39,7 +40,7 @@
 
 void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
     animator->incStrong(0);
-    animator->onAttached(&mParent);
+    animator->attach(&mParent);
     mNewAnimators.push_back(animator.get());
 }
 
@@ -58,7 +59,7 @@
         move_all(mNewAnimators, mAnimators);
     }
     for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
-        (*it)->pushStaging(&mParent, info);
+        (*it)->pushStaging(info);
     }
 }
 
@@ -68,7 +69,7 @@
             : mTarget(target), mInfo(info) {}
 
     bool operator() (BaseRenderNodeAnimator* animator) {
-        bool remove = animator->animate(&mTarget, mInfo);
+        bool remove = animator->animate(mInfo);
         if (remove) {
             animator->decStrong(0);
         }
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 57c66da..d553d10 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -427,24 +427,46 @@
         return 0;
     }
 
+    /**
+     * @hide
+     * Used to indicate that when parcelling, the tags should be parcelled through the flattened
+     * formatted string, not through the array of strings.
+     * Keep in sync with frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
+     * see definition of kAudioAttributesMarshallTagFlattenTags
+     */
+    public final static int FLATTEN_TAGS = 0x1;
+    /**
+     * When adding tags for writeToParcel(Parcel, int), add them in the list of flags (| NEW_FLAG)
+     */
+    private final static int ALL_PARCEL_FLAGS = FLATTEN_TAGS;
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mUsage);
         dest.writeInt(mContentType);
         dest.writeInt(mFlags);
-        String[] tagsArray = new String[mTags.size()];
-        mTags.toArray(tagsArray);
-        dest.writeStringArray(tagsArray);
+        dest.writeInt(flags & ALL_PARCEL_FLAGS);
+        if ((flags & FLATTEN_TAGS) == 0) {
+            String[] tagsArray = new String[mTags.size()];
+            mTags.toArray(tagsArray);
+            dest.writeStringArray(tagsArray);
+        } else if ((flags & FLATTEN_TAGS) == FLATTEN_TAGS) {
+            dest.writeString(mFormattedTags);
+        }
     }
 
     private AudioAttributes(Parcel in) {
         mUsage = in.readInt();
         mContentType = in.readInt();
         mFlags = in.readInt();
+        boolean hasFlattenedTags = ((in.readInt() & FLATTEN_TAGS) == FLATTEN_TAGS);
         mTags = new HashSet<String>();
-        String[] tagsArray = in.readStringArray();
-        for (int i = tagsArray.length - 1 ; i >= 0 ; i--) {
-            mTags.add(tagsArray[i]);
+        if (hasFlattenedTags) {
+            mTags.add(in.readString());
+        } else {
+            String[] tagsArray = in.readStringArray();
+            for (int i = tagsArray.length - 1 ; i >= 0 ; i--) {
+                mTags.add(tagsArray[i]);
+            }
         }
     }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c8b51e0..98a575a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -54,6 +54,7 @@
     private long mVolumeKeyUpTime;
     private final boolean mUseMasterVolume;
     private final boolean mUseVolumeKeySounds;
+    private final boolean mUseFixedVolume;
     private final Binder mToken = new Binder();
     private static String TAG = "AudioManager";
     AudioPortEventHandler mAudioPortEventHandler;
@@ -441,6 +442,8 @@
         mUseVolumeKeySounds = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useVolumeKeySounds);
         mAudioPortEventHandler = new AudioPortEventHandler(this);
+        mUseFixedVolume = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_useFixedVolume);
     }
 
     private static IAudioService getService()
@@ -577,6 +580,26 @@
     }
 
     /**
+     * Indicates if the device implements a fixed volume policy.
+     * <p>Some devices may not have volume control and may operate at a fixed volume,
+     * and may not enable muting or changing the volume of audio streams.
+     * This method will return true on such devices.
+     * <p>The following APIs have no effect when volume is fixed:
+     * <ul>
+     *   <li> {@link #adjustVolume(int, int)}
+     *   <li> {@link #adjustSuggestedStreamVolume(int, int, int)}
+     *   <li> {@link #adjustStreamVolume(int, int, int)}
+     *   <li> {@link #setStreamVolume(int, int, int)}
+     *   <li> {@link #setRingerMode(int)}
+     *   <li> {@link #setStreamSolo(int, boolean)}
+     *   <li> {@link #setStreamMute(int, boolean)}
+     * </ul>
+     */
+    public boolean isVolumeFixed() {
+        return mUseFixedVolume;
+    }
+
+    /**
      * Adjusts the volume of a particular stream by one step in a direction.
      * <p>
      * This method should only be used by applications that replace the platform-wide
@@ -614,7 +637,8 @@
      * <p>
      * This method should only be used by applications that replace the platform-wide
      * management of audio settings or the main telephony application.
-     *
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
      * @param direction The direction to adjust the volume. One of
      *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
      *            {@link #ADJUST_SAME}.
@@ -622,6 +646,7 @@
      * @see #adjustSuggestedStreamVolume(int, int, int)
      * @see #adjustStreamVolume(int, int, int)
      * @see #setStreamVolume(int, int, int)
+     * @see #isVolumeFixed()
      */
     public void adjustVolume(int direction, int flags) {
         IAudioService service = getService();
@@ -644,6 +669,8 @@
      * This method should only be used by applications that replace the platform-wide
      * management of audio settings or the main telephony application.
      *
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
      * @param direction The direction to adjust the volume. One of
      *            {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
      *            {@link #ADJUST_SAME}.
@@ -653,6 +680,7 @@
      * @see #adjustVolume(int, int)
      * @see #adjustStreamVolume(int, int, int)
      * @see #setStreamVolume(int, int, int)
+     * @see #isVolumeFixed()
      */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
         IAudioService service = getService();
@@ -801,10 +829,12 @@
      * Silent mode will mute the volume and will not vibrate. Vibrate mode will
      * mute the volume and vibrate. Normal mode will be audible and may vibrate
      * according to user settings.
-     *
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
      * @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL},
      *            {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}.
      * @see #getRingerMode()
+     * @see #isVolumeFixed()
      */
     public void setRingerMode(int ringerMode) {
         if (!isValidRingerMode(ringerMode)) {
@@ -820,13 +850,15 @@
 
     /**
      * Sets the volume index for a particular stream.
-     *
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
      * @param streamType The stream whose volume index should be set.
      * @param index The volume index to set. See
      *            {@link #getStreamMaxVolume(int)} for the largest valid value.
      * @param flags One or more flags.
      * @see #getStreamMaxVolume(int)
      * @see #getStreamVolume(int)
+     * @see #isVolumeFixed()
      */
     public void setStreamVolume(int streamType, int index, int flags) {
         IAudioService service = getService();
@@ -919,9 +951,13 @@
      * <p>
      * For a better user experience, applications MUST unsolo a soloed stream
      * in onPause() and solo is again in onResume() if appropriate.
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
      *
      * @param streamType The stream to be soloed/unsoloed.
      * @param state The required solo state: true for solo ON, false for solo OFF
+     *
+     * @see #isVolumeFixed()
      */
     public void setStreamSolo(int streamType, boolean state) {
         IAudioService service = getService();
@@ -948,9 +984,13 @@
      * <p>
      * This method should only be used by applications that replace the platform-wide
      * management of audio settings or the main telephony application.
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
      *
      * @param streamType The stream to be muted/unmuted.
      * @param state The required mute state: true for mute ON, false for mute OFF
+     *
+     * @see #isVolumeFixed()
      */
     public void setStreamMute(int streamType, boolean state) {
         IAudioService service = getService();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index d882bc8..628d35b 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -63,6 +63,8 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.System;
+
+import android.telecomm.TelecommManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -2725,17 +2727,13 @@
     }
 
     private boolean isInCommunication() {
-        boolean isOffhook = false;
+        boolean isInAPhoneCall = false;
 
-        if (mVoiceCapable) {
-            try {
-                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-                if (phone != null) isOffhook = phone.isOffhook();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Couldn't connect to phone service", e);
-            }
-        }
-        return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
+        TelecommManager telecommManager =
+                (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
+        isInAPhoneCall = telecommManager.isInAPhoneCall();
+
+        return (isInAPhoneCall || getMode() == AudioManager.MODE_IN_COMMUNICATION);
     }
 
     /**
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d1909bc..490a8fd 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1441,6 +1441,36 @@
 
     private native void _setAudioStreamType(int streamtype);
 
+    // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer.h
+    private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
+    /**
+     * Sets the parameter indicated by key.
+     * @param key key indicates the parameter to be set.
+     * @param value value of the parameter to be set.
+     * @return true if the parameter is set successfully, false otherwise
+     * {@hide}
+     */
+    private native boolean setParameter(int key, Parcel value);
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Must call this method before prepare() or
+     * prepareAsync() in order for the audio attributes to become effective
+     * thereafter.
+     * @param attributes a non-null set of audio attributes
+     */
+    public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException {
+        if (attributes == null) {
+            final String msg = "Cannot set audio attributes to null";
+            throw new IllegalArgumentException(msg);
+        }
+        Parcel pattributes = Parcel.obtain();
+        attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
+        setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
+        pattributes.recycle();
+    }
+
     /**
      * Sets the player to be looping or non-looping.
      *
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9516bf8..1255276 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1185,6 +1185,7 @@
         HashSet<String> existingFiles = new HashSet<String>();
         String directory = "/sdcard/DCIM/.thumbnails";
         String [] files = (new File(directory)).list();
+        Cursor c = null;
         if (files == null)
             files = new String[0];
 
@@ -1194,7 +1195,7 @@
         }
 
         try {
-            Cursor c = mMediaProvider.query(
+            c = mMediaProvider.query(
                     mPackageName,
                     mThumbsUri,
                     new String [] { "_data" },
@@ -1219,11 +1220,12 @@
             }
 
             Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
+        } catch (RemoteException e) {
+            // We will soon be killed...
+        } finally {
             if (c != null) {
                 c.close();
             }
-        } catch (RemoteException e) {
-            // We will soon be killed...
         }
     }
 
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6f42057..4587cf56 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -514,6 +514,25 @@
     return (jint) streamtype;
 }
 
+static jboolean
+android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
+{
+    ALOGV("setParameter: key %d", key);
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return false;
+    }
+
+    Parcel *request = parcelForJavaObject(env, java_request);
+    status_t err = mp->setParameter(key, *request);
+    if (err == OK) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
 static void
 android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
 {
@@ -857,6 +876,7 @@
     {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
     {"_setAudioStreamType", "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
     {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer_getAudioStreamType},
+    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
     {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index 0c65283..75278b5 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2014 The Android Open Source Project
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -66,6 +67,8 @@
 
     private boolean mGetOperation;
 
+    private boolean mGetFinalFlag;
+
     private HeaderSet mRequestHeader;
 
     private HeaderSet mReplyHeader;
@@ -90,6 +93,7 @@
         mOperationDone = false;
         mMaxPacketSize = maxSize;
         mGetOperation = type;
+        mGetFinalFlag = false;
 
         mPrivateInputOpen = false;
         mPrivateOutputOpen = false;
@@ -131,6 +135,15 @@
     }
 
     /**
+     * Allows to set flag which will force GET to be always sent as single packet request with
+     * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
+     * require requests to be sent this way.
+     */
+    public void setGetFinalFlag(boolean flag) {
+        mGetFinalFlag = flag;
+    }
+
+    /**
      * Sends an ABORT message to the server. By calling this method, the
      * corresponding input and output streams will be closed along with this
      * object.
@@ -551,15 +564,25 @@
 
         if (mGetOperation) {
             if (!mOperationDone) {
-                mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
-                while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x03);
-                }
+                if (!mGetFinalFlag) {
+                    mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+                    while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+                        more = sendRequest(0x03);
+                    }
 
-                if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                    mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
-                }
-                if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+                    if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+                        mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+                    }
+                    if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+                        mOperationDone = true;
+                    }
+                } else {
+                    more = sendRequest(0x83);
+
+                    if (more) {
+                        throw new IOException("FINAL_GET forced but data did not fit into single packet!");
+                    }
+
                     mOperationDone = true;
                 }
             }
@@ -613,7 +636,16 @@
                 if (mPrivateInput == null) {
                     mPrivateInput = new PrivateInputStream(this);
                 }
-                sendRequest(0x03);
+
+                if (!mGetFinalFlag) {
+                    sendRequest(0x03);
+                } else {
+                    sendRequest(0x83);
+
+                    if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+                        mOperationDone = true;
+                    }
+                }
                 return true;
 
             } else if (mOperationDone) {
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
index 2b3066f..51b560a 100644
--- a/obex/javax/obex/HeaderSet.java
+++ b/obex/javax/obex/HeaderSet.java
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2014 The Android Open Source Project
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -181,6 +182,8 @@
 
     private String mName; // null terminated Unicode text string
 
+    private boolean mEmptyName;
+
     private String mType; // null terminated ASCII text string
 
     private Long mLength; // 4 byte unsigend integer
@@ -235,6 +238,25 @@
     }
 
     /**
+     * Sets flag for special "value" of NAME header which should be empty. This
+     * is not the same as NAME header with empty string in which case it will
+     * have length of 5 bytes. It should be 3 bytes with only header id and
+     * length field.
+     */
+    public void setEmptyNameHeader() {
+        mName = null;
+        mEmptyName = true;
+    }
+
+    /**
+     * Gets flag for special "value" of NAME header which should be empty. See
+     * above.
+     */
+    public boolean getEmptyNameHeader() {
+        return mEmptyName;
+    }
+
+    /**
      * Sets the value of the header identifier to the value provided. The type
      * of object must correspond to the Java type defined in the description of
      * this interface. If <code>null</code> is passed as the
@@ -269,6 +291,7 @@
                 if ((headerValue != null) && (!(headerValue instanceof String))) {
                     throw new IllegalArgumentException("Name must be a String");
                 }
+                mEmptyName = false;
                 mName = (String)headerValue;
                 break;
             case TYPE:
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 8c12a20..0a06709 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2014 The Android Open Source Project
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -387,6 +388,11 @@
                 if (nullOut) {
                     headImpl.setHeader(HeaderSet.NAME, null);
                 }
+            } else if (headImpl.getEmptyNameHeader()) {
+                out.write((byte) HeaderSet.NAME);
+                lengthArray[0] = (byte) 0x00;
+                lengthArray[1] = (byte) 0x03;
+                out.write(lengthArray);
             }
 
             // Type Header
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 52db30a..168fb41 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -50,6 +50,7 @@
 
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.NativeLibraryHelper.ApkHandle;
 import com.android.internal.content.PackageHelper;
 
 import java.io.BufferedInputStream;
@@ -107,8 +108,27 @@
                 return null;
             }
 
-            return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
-                    isExternal, isForwardLocked, abiOverride);
+
+            if (isExternal) {
+                // Make sure the sdcard is mounted.
+                String status = Environment.getExternalStorageState();
+                if (!status.equals(Environment.MEDIA_MOUNTED)) {
+                    Slog.w(TAG, "Make sure sdcard is mounted.");
+                    return null;
+                }
+            }
+
+            ApkHandle handle = null;
+            try {
+                handle = ApkHandle.create(packageURI.getPath());
+                return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
+                        isExternal, isForwardLocked, handle, abiOverride);
+            } catch (IOException ioe) {
+                Slog.w(TAG, "Problem opening APK: " + packageURI.getPath());
+                return null;
+            } finally {
+                IoUtils.closeQuietly(handle);
+            }
         }
 
         /**
@@ -329,21 +349,11 @@
 
     private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName,
             String publicResFileName, boolean isExternal, boolean isForwardLocked,
-            String abiOverride) {
-
-        if (isExternal) {
-            // Make sure the sdcard is mounted.
-            String status = Environment.getExternalStorageState();
-            if (!status.equals(Environment.MEDIA_MOUNTED)) {
-                Slog.w(TAG, "Make sure sdcard is mounted.");
-                return null;
-            }
-        }
-
+            ApkHandle handle, String abiOverride) {
         // The .apk file
         String codePath = packageURI.getPath();
         File codeFile = new File(codePath);
-        NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
+
         String[] abiList = Build.SUPPORTED_ABIS;
         if (abiOverride != null) {
             abiList = new String[] { abiOverride };
@@ -850,14 +860,14 @@
 
     private int calculateContainerSize(File apkFile, boolean forwardLocked,
             String abiOverride) throws IOException {
-        NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile);
-        final int abi = NativeLibraryHelper.findSupportedAbi(handle,
-                (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
-
+        ApkHandle handle = null;
         try {
+            handle = ApkHandle.create(apkFile);
+            final int abi = NativeLibraryHelper.findSupportedAbi(handle,
+                    (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
             return calculateContainerSize(handle, apkFile, abi, forwardLocked);
         } finally {
-            handle.close();
+            IoUtils.closeQuietly(handle);
         }
     }
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e12549a..ad27e41 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -106,6 +106,8 @@
     <!-- Wifi Display -->
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
 
+    <uses-permission android:name="android.permission.CAMERA" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
new file mode 100644
index 0000000..49eba22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64.0dp"
+        android:height="64.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#4DFFFFFF"
+        android:pathData="M3.3,3.0L2.0,4.3l5.0,5.0L7.0,13.0l3.0,0.0l0.0,9.0l3.6,-6.1l4.1,4.1l1.3,-1.3L3.3,3.0zM17.0,10.0l-4.0,0.0l4.0,-8.0L7.0,2.0l0.0,2.2l8.5,8.5L17.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
new file mode 100644
index 0000000..101ca84
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64.0dp"
+        android:height="64.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M7.0,2.0l0.0,11.0 3.0,0.0 0.0,9.0 7.0,-12.0 -4.0,0.0 4.0,-8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2b7793d..2188a8e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -62,6 +62,14 @@
             android:layout_height="match_parent"
             android:layout_marginBottom="@dimen/close_handle_underlap"/>
 
+        <ViewStub
+                android:id="@+id/keyguard_user_switcher"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+                android:layout_gravity="end"
+                android:layout="@layout/keyguard_user_switcher" />
+
         <com.android.systemui.statusbar.phone.ObservableScrollView
             android:id="@+id/scroll_view"
             android:layout_width="match_parent"
@@ -92,14 +100,6 @@
 
     <include layout="@layout/status_bar_expanded_header" />
 
-    <ViewStub
-        android:id="@+id/keyguard_user_switcher"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
-        android:layout_gravity="end"
-        android:layout="@layout/keyguard_user_switcher" />
-
     <include
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f021253..a8799f7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -540,6 +540,8 @@
     <string name="quick_settings_hotspot_label">Hotspot</string>
     <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
     <string name="quick_settings_notifications_label">Notifications</string>
+    <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_flashlight_label">Flashlight</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">No recent apps</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 322174a..85f58881 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -881,8 +881,7 @@
             return;
         }
 
-        if (!mUserManager.isUserSwitcherEnabled()
-                && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
+        if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0720704..d152887 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -50,6 +50,7 @@
     private int mPanelPaddingBottom;
     private int mDualTileUnderlap;
     private boolean mExpanded;
+    private boolean mListening;
 
     private TileRecord mDetailRecord;
     private Callback mCallback;
@@ -100,9 +101,14 @@
         if (!mExpanded) {
             showDetail(false /*show*/, mDetailRecord);
         }
+    }
+
+    public void setListening(boolean listening) {
+        if (mListening == listening) return;
+        mListening = listening;
         for (TileRecord r : mRecords) {
-            r.tile.setListening(mExpanded);
-            if (mExpanded) {
+            r.tile.setListening(mListening);
+            if (mListening) {
                 r.tile.refreshState();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 786cd9e..ba350e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.Listenable;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -221,6 +222,7 @@
         TetheringController getTetheringController();
         CastController getCastController();
         VolumeComponent getVolumeComponent();
+        FlashlightController getFlashlightController();
     }
 
     public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
new file mode 100644
index 0000000..b610cf3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -0,0 +1,80 @@
+/*
+ * 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.qs.tiles;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.policy.FlashlightController;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings.Secure;
+
+/** Quick settings tile: Control flashlight **/
+public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
+        FlashlightController.FlashlightListener {
+
+    private final FlashlightController mFlashlightController;
+
+    public FlashlightTile(Host host) {
+        super(host);
+        mFlashlightController = host.getFlashlightController();
+        mFlashlightController.addListener(this);
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+    }
+
+    @Override
+    protected void handleClick() {
+        boolean newState = !mState.value;
+        mFlashlightController.setFlashlight(newState);
+        refreshState(newState);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        if (arg instanceof Boolean) {
+            state.value = (Boolean) arg;
+        }
+        state.visible = mFlashlightController.isAvailable();
+        state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
+        state.iconId = state.value
+                ? R.drawable.ic_qs_flashlight_on : R.drawable.ic_qs_flashlight_off;
+    }
+
+    @Override
+    public void onFlashlightOff() {
+        refreshState(false);
+    }
+
+    @Override
+    public void onFlashlightError() {
+        refreshState(false);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 95edd1c..4c52b24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -147,7 +147,9 @@
                 }
             } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
                 // Try and start the enter animation (or restart it on configuration changed)
-                mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
+                ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
+                mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
+                        mFullScreenOverlayView, t));
                 // Call our callback
                 onEnterAnimationTriggered();
             }
@@ -392,7 +394,9 @@
 
     void onConfigurationChange() {
         // Try and start the enter animation (or restart it on configuration changed)
-        mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
+        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
+        mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
+                mFullScreenOverlayView, t));
         // Call our callback
         onEnterAnimationTriggered();
     }
@@ -536,6 +540,12 @@
             final boolean backward = event.isShiftPressed();
             mRecentsView.focusNextTask(!backward);
             return true;
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+            mRecentsView.focusNextTask(true);
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+            mRecentsView.focusNextTask(false);
+            return true;
         }
         // Pass through the debug trigger
         mDebugTrigger.onKeyEvent(keyCode);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index e7eac67..b5b9cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -186,6 +186,10 @@
 
     /** Requests all task stacks to start their enter-recents animation */
     public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+        // Handle the case when there are no views by incrementing and decrementing after all
+        // animations are started.
+        ctx.postAnimationTrigger.increment();
+
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -194,6 +198,10 @@
                 stackView.startEnterRecentsAnimation(ctx);
             }
         }
+
+        // Handle the case when there are no views by incrementing and decrementing after all
+        // animations are started.
+        ctx.postAnimationTrigger.decrement();
     }
 
     /** Requests all task stacks to start their exit-recents animation */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 29eaa9e..2c05daa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -747,9 +747,6 @@
             // Mark that we have completely the first layout
             mAwaitingFirstLayout = false;
 
-            // Start dozing
-            mUIDozeTrigger.startDozing();
-
             // Prepare the first view for its enter animation
             int offsetTopAlign = -mStackAlgorithm.mTaskRect.top;
             int offscreenY = mStackAlgorithm.mRect.bottom -
@@ -770,7 +767,11 @@
 
             // Update the focused task index to be the next item to the top task
             if (mConfig.launchedWithAltTab) {
+                // When alt-tabbing, we focus the next previous task
                 focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
+            } else {
+                // Normally we just focus the front task
+                focusTask(Math.max(0, mStack.getTaskCount() - 1), false);
             }
         }
     }
@@ -799,6 +800,15 @@
                     mStack.indexOfTask(tv.getTask()), getStackScroll());
             tv.startEnterRecentsAnimation(ctx);
         }
+
+        // Add a runnable to the post animation ref counter to clear all the views
+        ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                // Start dozing
+                mUIDozeTrigger.startDozing();
+            }
+        });
     }
 
     /** Requests this task stacks to start it's exit-recents animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b1fc700..c6bacbd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -296,7 +298,7 @@
     }
 
     /** Animates this task view as it enters recents */
-    public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+    public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
         TaskViewTransform transform = ctx.transform;
 
         if (mConfig.launchedFromAppWithScreenshot) {
@@ -308,6 +310,8 @@
                         // Animate the task bar of the first task view
                         mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
                         setVisibility(View.VISIBLE);
+                        // Decrement the post animation trigger
+                        ctx.postAnimationTrigger.decrement();
                     }
                 });
             } else {
@@ -321,9 +325,17 @@
                         .setInterpolator(mConfig.linearOutSlowInInterpolator)
                         .setDuration(475)
                         .withLayer()
-                        .withEndAction(mEnableThumbnailClip)
+                        .withEndAction(new Runnable() {
+                            @Override
+                            public void run() {
+                                mEnableThumbnailClip.run();
+                                // Decrement the post animation trigger
+                                ctx.postAnimationTrigger.decrement();
+                            }
+                        })
                         .start();
             }
+            ctx.postAnimationTrigger.increment();
 
         } else if (mConfig.launchedFromAppWithThumbnail) {
             if (ctx.isFrontMost) {
@@ -335,7 +347,15 @@
                 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
                 anim.setDuration(mConfig.taskBarEnterAnimDuration);
                 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        // Decrement the post animation trigger
+                        ctx.postAnimationTrigger.decrement();
+                    }
+                });
                 anim.start();
+                ctx.postAnimationTrigger.increment();
             } else {
                 mEnableThumbnailClip.run();
             }
@@ -355,8 +375,16 @@
                     .setInterpolator(mConfig.quintOutInterpolator)
                     .setDuration(mConfig.taskViewEnterFromHomeDuration)
                     .withLayer()
-                    .withEndAction(mEnableThumbnailClip)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mEnableThumbnailClip.run();
+                            // Decrement the post animation trigger
+                            ctx.postAnimationTrigger.decrement();
+                        }
+                    })
                     .start();
+            ctx.postAnimationTrigger.increment();
         }
     }
 
@@ -518,7 +546,12 @@
      */
     public void setFocusedTask() {
         mIsFocused = true;
+        // Workaround, we don't always want it focusable in touch mode, but we want the first task
+        // to be focused after the enter-recents animation, which can be triggered from either touch
+        // or keyboard
+        setFocusableInTouchMode(true);
         requestFocus();
+        setFocusableInTouchMode(false);
         invalidate();
         mCb.onTaskFocused(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index 13407aa..fa97d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -38,9 +38,13 @@
         int stackViewCount;
         // Whether this is the front most task view
         boolean isFrontMost;
+        // A trigger to run some logic when all the animations complete.  This works around the fact
+        // that it is difficult to coordinate ViewPropertyAnimators
+        ReferenceCountedTrigger postAnimationTrigger;
 
-        public TaskViewEnterContext(FullscreenTransitionOverlayView fss) {
+        public TaskViewEnterContext(FullscreenTransitionOverlayView fss, ReferenceCountedTrigger t) {
             fullScreenshot = fss;
+            postAnimationTrigger = t;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c82bdf6..b2e79c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -35,6 +35,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
@@ -61,7 +62,7 @@
     private KeyguardPageSwipeHelper mPageSwiper;
     private StatusBarHeaderView mHeader;
     private View mQsContainer;
-    private View mQsPanel;
+    private QSPanel mQsPanel;
     private View mKeyguardStatusView;
     private ObservableScrollView mScrollView;
     private TextView mClockView;
@@ -77,6 +78,12 @@
     private boolean mQsTracking;
 
     /**
+     * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
+     * the expansion for quick settings.
+     */
+    private boolean mConflictingQsExpansionGesture;
+
+    /**
      * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
      * intercepted yet.
      */
@@ -94,6 +101,7 @@
     private int mQsMaxExpansionHeight;
     private int mQsPeekHeight;
     private boolean mStackScrollerOverscrolling;
+    private boolean mQsExpansionFromOverscroll;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -136,7 +144,7 @@
         mHeader.setOverlayParent(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
         mQsContainer = findViewById(R.id.quick_settings_container);
-        mQsPanel = findViewById(R.id.quick_settings_panel);
+        mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
         mClockView = (TextView) findViewById(R.id.clock_view);
         mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
         mScrollView.setListener(this);
@@ -422,11 +430,21 @@
 
     private void flingQsWithCurrentVelocity() {
         float vel = getCurrentVelocity();
-
-        // TODO: Better logic whether we should expand or not.
-        flingSettings(vel, vel > 0);
+        flingSettings(vel, flingExpandsQs(vel));
     }
 
+    private boolean flingExpandsQs(float vel) {
+        if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+            return getQsExpansionFraction() > 0.5f;
+        } else {
+            return vel > 0;
+        }
+    }
+
+    private float getQsExpansionFraction() {
+        return (mQsExpansionHeight - mQsMinExpansionHeight)
+                / (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+    }
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mBlockTouches) {
@@ -442,20 +460,48 @@
                 return true;
             }
         }
-        if (mQsTracking || mQsExpanded) {
-            return onQsTouch(event);
-        }
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
+                && mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
 
+            // Down in the empty area while fully expanded - go to QS.
+            mQsTracking = true;
+            mConflictingQsExpansionGesture = true;
+            onQsExpansionStarted();
+            mInitialHeightOnTouch = mQsExpansionHeight;
+            mInitialTouchY = event.getX();
+            mInitialTouchX = event.getY();
+        }
+        if (mQsTracking || mQsExpanded) {
+            onQsTouch(event);
+            if (!mConflictingQsExpansionGesture) {
+                return true;
+            }
+        }
+        if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+                || event.getActionMasked() == MotionEvent.ACTION_UP) {
+            mConflictingQsExpansionGesture = false;
+        }
         super.onTouchEvent(event);
         return true;
     }
 
     @Override
+    protected boolean flingExpands(float vel, float vectorVel) {
+        boolean expands = super.flingExpands(vel, vectorVel);
+
+        // If we are already running a QS expansion, make sure that we keep the panel open.
+        if (mQsExpansionAnimator != null) {
+            expands = true;
+        }
+        return expands;
+    }
+
+    @Override
     protected boolean hasConflictingGestures() {
         return mStatusBar.getBarState() != StatusBarState.SHADE;
     }
 
-    private boolean onQsTouch(MotionEvent event) {
+    private void onQsTouch(MotionEvent event) {
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -500,14 +546,17 @@
                 mQsTracking = false;
                 mTrackingPointer = -1;
                 trackMovement(event);
-                flingQsWithCurrentVelocity();
+                float fraction = getQsExpansionFraction();
+                if ((fraction != 0f || y >= mInitialTouchY)
+                        && (fraction != 1f || y <= mInitialTouchY)) {
+                    flingQsWithCurrentVelocity();
+                }
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
                 }
                 break;
         }
-        return true;
     }
 
     @Override
@@ -523,19 +572,25 @@
 
 
     @Override
-    public void onOverscrollTopChanged(float amount) {
+    public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
         cancelAnimation();
         float rounded = amount >= 1f ? amount : 0f;
-        mStackScrollerOverscrolling = rounded != 0f;
+        mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
+        mQsExpansionFromOverscroll = rounded != 0f;
         setQsExpansion(mQsMinExpansionHeight + rounded);
         updateQsState();
     }
 
     @Override
     public void flingTopOverscroll(float velocity, boolean open) {
-        mStackScrollerOverscrolling = false;
         setQsExpansion(mQsExpansionHeight);
-        flingSettings(velocity, open);
+        flingSettings(velocity, open, new Runnable() {
+            @Override
+            public void run() {
+                mStackScrollerOverscrolling = false;
+                mQsExpansionFromOverscroll = false;
+            }
+        });
     }
 
     private void onQsExpansionStarted() {
@@ -571,13 +626,12 @@
     private void updateQsState() {
         boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
         mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
-        mNotificationStackScroller.setEnabled(!mQsExpanded);
+        mNotificationStackScroller.setEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
         mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
-        mQsContainer.setVisibility(mKeyguardShowing && !expandVisually
-                ? View.INVISIBLE
-                : View.VISIBLE);
+        mQsContainer.setVisibility(
+                mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
         mScrollView.setTouchEnabled(mQsExpanded);
-        mNotificationStackScroller.setTouchEnabled(!mQsExpanded);
+        mNotificationStackScroller.setTouchEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
     }
 
     private void setQsExpansion(float height) {
@@ -640,9 +694,17 @@
             mQsExpansionAnimator.cancel();
         }
     }
+
     private void flingSettings(float vel, boolean expand) {
+        flingSettings(vel, expand, null);
+    }
+
+    private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) {
         float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
         if (target == mQsExpansionHeight) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
             return;
         }
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
@@ -657,6 +719,9 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mQsExpansionAnimator = null;
+                if (onFinishRunnable != null) {
+                    onFinishRunnable.run();
+                }
             }
         });
         animator.start();
@@ -873,10 +938,27 @@
         super.onExpandingFinished();
         mNotificationStackScroller.onExpansionStopped();
         mIsExpanding = false;
+        if (mExpandedHeight == 0f) {
+            mHeader.setListening(false);
+            mQsPanel.setListening(false);
+        } else {
+            mHeader.setListening(true);
+            mQsPanel.setListening(true);
+        }
+    }
+
+    @Override
+    public void instantExpand() {
+        super.instantExpand();
+        mHeader.setListening(true);
+        mQsPanel.setListening(true);
     }
 
     @Override
     protected void setOverExpansion(float overExpansion, boolean isPixels) {
+        if (mConflictingQsExpansionGesture) {
+            return;
+        }
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
             mNotificationStackScroller.setOnHeightChangedListener(null);
             if (isPixels) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 5b47749..7d5d99d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -301,6 +301,8 @@
                 mTrackingPointer = -1;
                 trackMovement(event);
                 if ((mTracking && mTouchSlopExceeded)
+                        || Math.abs(x - mInitialTouchX) > mTouchSlop
+                        || Math.abs(y - mInitialTouchY) > mTouchSlop
                         || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
                     float vel = 0f;
                     float vectorVel = 0f;
@@ -317,6 +319,7 @@
                     boolean expands = onEmptySpaceClick(mInitialTouchX);
                     onTrackingStopped(expands);
                 }
+
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
@@ -447,7 +450,7 @@
      * @param vectorVel the length of the vectorial velocity
      * @return whether a fling should expands the panel; contracts otherwise
      */
-    private boolean flingExpands(float vel, float vectorVel) {
+    protected boolean flingExpands(float vel, float vectorVel) {
         if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
             return getExpandedFraction() > 0.5f;
         } else {
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 f70010e..387f5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -31,7 +31,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
@@ -123,6 +122,7 @@
 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -194,6 +194,7 @@
     CastControllerImpl mCastController;
     VolumeComponent mVolumeComponent;
     KeyguardUserSwitcher mKeyguardUserSwitcher;
+    FlashlightController mFlashlightController;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -518,7 +519,7 @@
         Resources res = context.getResources();
 
         updateDisplaySize(); // populates mDisplayMetrics
-        loadDimens();
+        updateResources();
 
         mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
 
@@ -711,6 +712,7 @@
         }
 
         mBatteryController.setStatusBarHeaderView(mHeader);
+        mFlashlightController = new FlashlightController(mContext);
 
         // Set up the quick settings tile panel
         mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
@@ -726,7 +728,7 @@
             final QSTileHost qsh = new QSTileHost(mContext, this,
                     mBluetoothController, mLocationController, mRotationLockController,
                     mNetworkController, mZenModeController, null /*tethering*/,
-                    mCastController, mVolumeComponent);
+                    mCastController, mVolumeComponent, mFlashlightController);
             for (QSTile<?> tile : qsh.getTiles()) {
                 mQSPanel.addTile(tile);
             }
@@ -3183,7 +3185,7 @@
             }
             final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
             mVisibleNotifications.clear();
-            mVisibleNotifications.putAll(mNewVisibleNotifications);
+            mVisibleNotifications.addAll(mNewVisibleNotifications);
 
             // We have new notifications
             if (updates && mDozeServiceHost != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7c87580..60f38b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -29,6 +29,7 @@
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.FlashlightTile;
 import com.android.systemui.qs.tiles.LocationTile;
 import com.android.systemui.qs.tiles.NotificationsTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
@@ -37,6 +38,7 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RotationLockController;
@@ -64,12 +66,13 @@
     private final VolumeComponent mVolume;
     private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
     private final int mFeedbackStartDelay;
+    private final FlashlightController mFlashlight;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
             BluetoothController bluetooth, LocationController location,
             RotationLockController rotation, NetworkController network,
             ZenModeController zen, TetheringController tethering,
-            CastController cast, VolumeComponent volume) {
+            CastController cast, VolumeComponent volume, FlashlightController flashlight) {
         mContext = context;
         mStatusBar = statusBar;
         mBluetooth = bluetooth;
@@ -80,6 +83,7 @@
         mTethering = tethering;
         mCast = cast;
         mVolume = volume;
+        mFlashlight = flashlight;
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
         ht.start();
@@ -95,6 +99,7 @@
         mTiles.add(new LocationTile(this));
         mTiles.add(new CastTile(this));
         mTiles.add(new HotspotTile(this));
+        mTiles.add(new FlashlightTile(this));
 
         mUserTracker = new CurrentUserTracker(mContext) {
             @Override
@@ -177,4 +182,9 @@
     public VolumeComponent getVolumeComponent() {
         return mVolume;
     }
+
+    @Override
+    public FlashlightController getFlashlightController() {
+        return mFlashlight;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 9e9de5d..3e2dcef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -45,6 +45,7 @@
     private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
 
     private boolean mExpanded;
+    private boolean mListening;
     private boolean mOverscrolled;
     private boolean mKeyguardShowing;
 
@@ -145,6 +146,14 @@
         return mExpandedHeight;
     }
 
+    public void setListening(boolean listening) {
+        if (listening == mListening) {
+            return;
+        }
+        mListening = listening;
+        updateBrightnessControllerState();
+    }
+
     public void setExpanded(boolean expanded, boolean overscrolled) {
         boolean changed = expanded != mExpanded;
         boolean overscrollChanged = overscrolled != mOverscrolled;
@@ -154,7 +163,6 @@
             updateHeights();
             updateVisibilities();
             updateSystemIconsLayoutParams();
-            updateBrightnessControllerState();
             updateZTranslation();
             updateClickTargets();
             updateWidth();
@@ -254,7 +262,7 @@
     }
 
     private void updateBrightnessControllerState() {
-        if (mExpanded) {
+        if (mListening) {
             mBrightnessController.registerCallbacks();
         } else {
             mBrightnessController.unregisterCallbacks();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
new file mode 100644
index 0000000..b059043
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -0,0 +1,308 @@
+/*
+ * 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.policy;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Manages the flashlight.
+ */
+public class FlashlightController {
+
+    private static final String TAG = "FlashlightController";
+
+    private final CameraManager mCameraManager;
+    /** Call {@link #ensureHandler()} before using */
+    private Handler mHandler;
+
+    /** Lock on mListeners when accessing */
+    private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
+
+    /** Lock on {@code this} when accessing */
+    private boolean mFlashlightEnabled;
+
+    private CameraDevice mCameraDevice;
+    private CaptureRequest mFlashlightRequest;
+    private CameraCaptureSession mSession;
+    private SurfaceTexture mSurfaceTexture;
+    private Surface mSurface;
+
+    public FlashlightController(Context mContext) {
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+    }
+
+    public synchronized void setFlashlight(boolean enabled) {
+        if (mFlashlightEnabled != enabled) {
+            mFlashlightEnabled = enabled;
+            postUpdateFlashlight();
+        }
+    }
+
+    public boolean isAvailable() {
+        try {
+            return getCameraId() != null;
+        } catch (CameraAccessException e) {
+            return false;
+        }
+    }
+
+    public void addListener(FlashlightListener l) {
+        synchronized (mListeners) {
+            cleanUpListenersLocked(l);
+            mListeners.add(new WeakReference<>(l));
+        }
+    }
+
+    public void removeListener(FlashlightListener l) {
+        synchronized (mListeners) {
+            cleanUpListenersLocked(l);
+        }
+    }
+
+    private synchronized void ensureHandler() {
+        if (mHandler == null) {
+            HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+            thread.start();
+            mHandler = new Handler(thread.getLooper());
+        }
+    }
+
+    private void startDevice() throws CameraAccessException {
+        mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
+    }
+
+    private void startSession() throws CameraAccessException {
+        mSurfaceTexture = new SurfaceTexture(false);
+        Size size = getSmallestSize(mCameraDevice.getId());
+        mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
+        mSurface = new Surface(mSurfaceTexture);
+        ArrayList<Surface> outputs = new ArrayList<>(1);
+        outputs.add(mSurface);
+        mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
+    }
+
+    private Size getSmallestSize(String cameraId) throws CameraAccessException {
+        Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
+                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+                .getOutputSizes(SurfaceTexture.class);
+        if (outputSizes == null || outputSizes.length == 0) {
+            throw new IllegalStateException(
+                    "Camera " + cameraId + "doesn't support any outputSize.");
+        }
+        Size chosen = outputSizes[0];
+        for (Size s : outputSizes) {
+            if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
+                chosen = s;
+            }
+        }
+        return chosen;
+    }
+
+    private void postUpdateFlashlight() {
+        ensureHandler();
+        mHandler.post(mUpdateFlashlightRunnable);
+    }
+
+    private String getCameraId() throws CameraAccessException {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (String id : ids) {
+            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
+            Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+            Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
+            if (flashAvailable != null && flashAvailable
+                    && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+                return id;
+            }
+        }
+        return null;
+    }
+
+    private void updateFlashlight(boolean forceDisable) {
+        try {
+            boolean enabled;
+            synchronized (this) {
+                enabled = mFlashlightEnabled && !forceDisable;
+            }
+            if (enabled) {
+                if (mCameraDevice == null) {
+                    startDevice();
+                    return;
+                }
+                if (mSession == null) {
+                    startSession();
+                    return;
+                }
+                if (mFlashlightRequest == null) {
+                    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
+                            CameraDevice.TEMPLATE_PREVIEW);
+                    builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
+                    builder.addTarget(mSurface);
+                    CaptureRequest request = builder.build();
+                    mSession.capture(request, null, mHandler);
+                    mFlashlightRequest = request;
+                }
+            } else {
+                if (mCameraDevice != null) {
+                    mCameraDevice.close();
+                    teardown();
+                }
+            }
+
+        } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
+            Log.e(TAG, "Error in updateFlashlight", e);
+            handleError();
+        }
+    }
+
+    private void teardown() {
+        mCameraDevice = null;
+        mSession = null;
+        mFlashlightRequest = null;
+        if (mSurface != null) {
+            mSurface.release();
+            mSurfaceTexture.release();
+        }
+        mSurface = null;
+        mSurfaceTexture = null;
+    }
+
+    private void handleError() {
+        synchronized (this) {
+            mFlashlightEnabled = false;
+        }
+        dispatchError();
+        dispatchOff();
+        updateFlashlight(true /* forceDisable */);
+    }
+
+    private void dispatchOff() {
+        dispatchListeners(false, true /* off */);
+    }
+
+    private void dispatchError() {
+        dispatchListeners(true /* error */, false);
+    }
+
+    private void dispatchListeners(boolean error, boolean off) {
+        synchronized (mListeners) {
+            final int N = mListeners.size();
+            boolean cleanup = false;
+            for (int i = 0; i < N; i++) {
+                FlashlightListener l = mListeners.get(i).get();
+                if (l != null) {
+                    if (error) {
+                        l.onFlashlightError();
+                    } else if (off) {
+                        l.onFlashlightOff();
+                    }
+                } else {
+                    cleanup = true;
+                }
+            }
+            if (cleanup) {
+                cleanUpListenersLocked(null);
+            }
+        }
+    }
+
+    private void cleanUpListenersLocked(FlashlightListener listener) {
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            FlashlightListener found = mListeners.get(i).get();
+            if (found == null || found == listener) {
+                mListeners.remove(i);
+            }
+        }
+    }
+
+    private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
+        @Override
+        public void onOpened(CameraDevice camera) {
+            mCameraDevice = camera;
+            postUpdateFlashlight();
+        }
+
+        @Override
+        public void onDisconnected(CameraDevice camera) {
+            if (mCameraDevice == camera) {
+                dispatchOff();
+                teardown();
+            }
+        }
+
+        @Override
+        public void onError(CameraDevice camera, int error) {
+            Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
+            if (camera == mCameraDevice || mCameraDevice == null) {
+                handleError();
+            }
+        }
+    };
+
+    private final CameraCaptureSession.StateListener mSessionListener =
+            new CameraCaptureSession.StateListener() {
+        @Override
+        public void onConfigured(CameraCaptureSession session) {
+            mSession = session;
+            postUpdateFlashlight();
+        }
+
+        @Override
+        public void onConfigureFailed(CameraCaptureSession session) {
+            Log.e(TAG, "Configure failed.");
+            if (mSession == null || mSession == session) {
+                handleError();
+            }
+        }
+    };
+
+    private final Runnable mUpdateFlashlightRunnable = new Runnable() {
+        @Override
+        public void run() {
+            updateFlashlight(false /* forceDisable */);
+        }
+    };
+
+    public interface FlashlightListener {
+
+        /**
+         * Called when the flashlight turns off unexpectedly.
+         */
+        void onFlashlightOff();
+
+        /**
+         * Called when there is an error that turns the flashlight off.
+         */
+        void onFlashlightError();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c9a1b28..94fdd1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -988,34 +988,50 @@
      */
     public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
             boolean cancelAnimators) {
+        setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     * @param isRubberbanded The value which will be passed to
+     *                     {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators, boolean isRubberbanded) {
         if (cancelAnimators) {
             mStateAnimator.cancelOverScrollAnimators(onTop);
         }
-        setOverScrollAmountInternal(amount, onTop, animate);
+        setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
     }
 
-    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
+            boolean isRubberbanded) {
         amount = Math.max(0, amount);
         if (animate) {
-            mStateAnimator.animateOverScrollToAmount(amount, onTop);
+            mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
         } else {
             setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
             mAmbientState.setOverScrollAmount(amount, onTop);
             if (onTop) {
-                notifyOverscrollTopListener(amount);
+                notifyOverscrollTopListener(amount, isRubberbanded);
             }
             requestChildrenUpdate();
         }
     }
 
-    private void notifyOverscrollTopListener(float amount) {
+    private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
         mExpandHelper.onlyObserveMovements(amount > 1.0f);
         if (mDontReportNextOverScroll) {
             mDontReportNextOverScroll = false;
             return;
         }
         if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
+            mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
         }
     }
 
@@ -1061,9 +1077,9 @@
                 updateChildren();
                 float overScrollTop = getCurrentOverScrollAmount(true);
                 if (mOwnScrollY < 0) {
-                    notifyOverscrollTopListener(-mOwnScrollY);
+                    notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
                 } else {
-                    notifyOverscrollTopListener(overScrollTop);
+                    notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
                 }
             }
         } else {
@@ -1288,6 +1304,16 @@
         return RUBBER_BAND_FACTOR_NORMAL;
     }
 
+    /**
+     * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
+     * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
+     * overscroll view (e.g. expand QS).
+     */
+    private boolean isRubberbanded(boolean onTop) {
+        return !onTop || mExpandedInThisMotion || mIsExpansionChanging
+                || !mScrolledToTopOnFirstDown;
+    }
+
     private void endDrag() {
         setIsBeingDragged(false);
 
@@ -1880,7 +1906,16 @@
      * A listener that gets notified when the overscroll at the top has changed.
      */
     public interface OnOverscrollTopChangedListener {
-        public void onOverscrollTopChanged(float amount);
+
+        /**
+         * Notifies a listener that the overscroll has changed.
+         *
+         * @param amount the amount of overscroll, in pixels
+         * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+         *                     unrubberbanded motion to directly expand overscroll view (e.g expand
+         *                     QS)
+         */
+        public void onOverscrollTopChanged(float amount, boolean isRubberbanded);
 
         /**
          * Notify a listener that the scroller wants to escape from the scrolling motion and
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 494195e..f732cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -719,7 +719,8 @@
         }
     }
 
-    public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
+            final boolean isRubberbanded) {
         final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
         if (targetAmount == startOverScrollAmount) {
             return;
@@ -733,7 +734,8 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 float currentOverScroll = (float) animation.getAnimatedValue();
                 mHostLayout.setOverScrollAmount(
-                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */);
+                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
+                        isRubberbanded);
             }
         });
         overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 149f4aa..adfa1f2 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -32,20 +32,15 @@
 import com.android.internal.view.menu.MenuDialogHelper;
 import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
-import com.android.internal.widget.ActionBarContainer;
 import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.ActionBarOverlayLayout;
-import com.android.internal.widget.ActionBarView;
 import com.android.internal.widget.DecorContentParent;
 import com.android.internal.widget.SwipeDismissLayout;
 
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -122,6 +117,11 @@
 
     private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
 
+    private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
+            (1 << FEATURE_CUSTOM_TITLE) |
+            (1 << FEATURE_CONTENT_TRANSITIONS) |
+            (1 << FEATURE_ACTION_MODE_OVERLAY);
+
     /**
      * Simple callback used by the context menu and its submenus. The options
      * menu submenus do not use this (their behavior is more complex).
@@ -275,16 +275,13 @@
             throw new AndroidRuntimeException("requestFeature() must be called before adding content");
         }
         final int features = getFeatures();
-        if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) {
-
-            /* Another feature is enabled and the user is trying to enable the custom title feature */
-            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
-        }
-        if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) &&
-                (featureId != FEATURE_CUSTOM_TITLE) && (featureId != FEATURE_ACTION_MODE_OVERLAY)) {
-
-            /* Custom title feature is enabled and the user is trying to enable another feature */
-            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
+        final int newFeatures = features | (1 << featureId);
+        if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 &&
+                (newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) {
+            // Another feature is enabled and the user is trying to enable the custom title feature
+            // or custom title feature is enabled and the user is trying to enable another feature
+            throw new AndroidRuntimeException(
+                    "You cannot combine custom titles with other title features");
         }
         if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
             return false; // Ignore. No title dominates.
@@ -2647,25 +2644,29 @@
             return false;
         }
 
-        private void updateColorViews(WindowInsets insets) {
-            if (mIsFloating || !ActivityManager.isHighEndGfx()) {
-                // No colors on floating windows or low end devices :(
-                return;
+        private WindowInsets updateColorViews(WindowInsets insets) {
+            if (!mIsFloating && ActivityManager.isHighEndGfx()) {
+                if (insets != null) {
+                    mLastTopInset = insets.getStableInsetTop();
+                    mLastBottomInset = insets.getStableInsetBottom();
+                }
+                mStatusColorView = updateColorViewInt(mStatusColorView,
+                        SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+                        mStatusBarColor, mLastTopInset, Gravity.TOP,
+                        STATUS_BAR_BACKGROUND_TRANSITION_NAME);
+                mNavigationColorView = updateColorViewInt(mNavigationColorView,
+                        SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+                        mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM,
+                        NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
             }
             if (insets != null) {
-                mLastTopInset = insets.getSystemWindowInsetTop();
-                mLastBottomInset = insets.getSystemWindowInsetBottom();
+                insets = insets.consumeStableInsets();
             }
-            mStatusColorView = updateColorViewInt(mStatusColorView,
-                    SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
-                    mStatusBarColor, mLastTopInset, Gravity.TOP);
-            mNavigationColorView = updateColorViewInt(mNavigationColorView,
-                    SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
-                    mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM);
+            return insets;
         }
 
         private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag,
-                int color, int height, int verticalGravity) {
+                int color, int height, int verticalGravity, String transitionName) {
             boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0
                     && (getAttributes().flags & translucentFlag) == 0
                     && (color & Color.BLACK) != 0
@@ -2675,6 +2676,7 @@
                 if (show) {
                     view = new View(mContext);
                     view.setBackgroundColor(color);
+                    view.setTransitionName(transitionName);
                     addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height,
                             Gravity.START | verticalGravity));
                 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ddfa9c1..4458a8d 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -64,7 +64,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
-import android.telephony.TelephonyManager;
+import android.telecomm.TelecommManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -409,6 +409,7 @@
     static final Rect mTmpContentFrame = new Rect();
     static final Rect mTmpVisibleFrame = new Rect();
     static final Rect mTmpDecorFrame = new Rect();
+    static final Rect mTmpStableFrame = new Rect();
     static final Rect mTmpNavigationFrame = new Rect();
 
     WindowState mTopFullscreenOpaqueWindowState;
@@ -1364,6 +1365,7 @@
                 // XXX right now the app process has complete control over
                 // this...  should introduce a token to let the system
                 // monitor/control what they are doing.
+                outAppOp[0] = AppOpsManager.OP_TOAST_WINDOW;
                 break;
             case TYPE_DREAM:
             case TYPE_INPUT_METHOD:
@@ -2038,8 +2040,8 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
     }
 
-    TelephonyManager getTelephonyService() {
-        return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+    TelecommManager getTelecommService() {
+        return (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
     }
 
     static IAudioService getAudioService() {
@@ -2126,10 +2128,10 @@
                 }
 
                 // If an incoming call is ringing, HOME is totally disabled.
-                // (The user is already on the InCallScreen at this point,
+                // (The user is already on the InCallUI at this point,
                 // and his ONLY options are to answer or reject the call.)
-                TelephonyManager telephonyManager = getTelephonyService();
-                if (telephonyManager != null && telephonyManager.isRinging()) {
+                TelecommManager telecommManager = getTelecommService();
+                if (telecommManager != null && telecommManager.isRinging()) {
                     Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
                     return -1;
                 }
@@ -2380,9 +2382,7 @@
         if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
             if (mRecentAppsHeldModifiers == 0 && !keyguardOn) {
                 final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
-                if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
-                        || KeyEvent.metaStateHasModifiers(
-                                shiftlessModifiers, KeyEvent.META_META_ON)) {
+                if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
                     mRecentAppsHeldModifiers = shiftlessModifiers;
                     showRecentApps(true);
                     return -1;
@@ -2975,7 +2975,8 @@
                 mStatusBarLayer = mNavigationBar.getSurfaceLayer();
                 // And compute the final frame.
                 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
-                        mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf);
+                        mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+                        mTmpNavigationFrame);
                 if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
                 if (mNavigationBarController.checkHiddenLw()) {
                     updateSysUiVisibility = true;
@@ -3000,7 +3001,7 @@
                 mStatusBarLayer = mStatusBar.getSurfaceLayer();
 
                 // Let the status bar determine its size.
-                mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf);
+                mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf, vf);
 
                 // For layout, the status bar is always at the top with our fixed height.
                 mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
@@ -3160,6 +3161,7 @@
         final Rect cf = mTmpContentFrame;
         final Rect vf = mTmpVisibleFrame;
         final Rect dcf = mTmpDecorFrame;
+        final Rect sf = mTmpStableFrame;
         dcf.setEmpty();
 
         final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
@@ -3167,6 +3169,12 @@
 
         final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
 
+        if (isDefaultDisplay) {
+            sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
+        } else {
+            sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
+        }
+
         if (!isDefaultDisplay) {
             if (attached != null) {
                 // If this window is attached to another, our display
@@ -3529,9 +3537,10 @@
                 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                 + " of=" + of.toShortString()
                 + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
-                + " dcf=" + dcf.toShortString());
+                + " dcf=" + dcf.toShortString()
+                + " sf=" + sf.toShortString());
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf);
+        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf);
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
@@ -4152,9 +4161,9 @@
                     }
                 }
                 if (down) {
-                    TelephonyManager telephonyManager = getTelephonyService();
-                    if (telephonyManager != null) {
-                        if (telephonyManager.isRinging()) {
+                    TelecommManager telecommManager = getTelecommService();
+                    if (telecommManager != null) {
+                        if (telecommManager.isRinging()) {
                             // If an incoming call is ringing, either VOLUME key means
                             // "silence ringer".  We handle these keys here, rather than
                             // in the InCallScreen, to make sure we'll respond to them
@@ -4166,14 +4175,14 @@
 
                             // Silence the ringer.  (It's safe to call this
                             // even if the ringer has already been silenced.)
-                            telephonyManager.silenceRinger();
+                            telecommManager.silenceRinger();
 
                             // And *don't* pass this key thru to the current activity
                             // (which is probably the InCallScreen.)
                             result &= ~ACTION_PASS_TO_USER;
                             break;
                         }
-                        if (telephonyManager.isOffhook()
+                        if (telecommManager.isInAPhoneCall()
                                 && (result & ACTION_PASS_TO_USER) == 0) {
                             // If we are in call but we decided not to pass the key to
                             // the application, just pass it to the session service.
@@ -4199,10 +4208,10 @@
             case KeyEvent.KEYCODE_ENDCALL: {
                 result &= ~ACTION_PASS_TO_USER;
                 if (down) {
-                    TelephonyManager telephonyManager = getTelephonyService();
+                    TelecommManager telecommManager = getTelecommService();
                     boolean hungUp = false;
-                    if (telephonyManager != null) {
-                        hungUp = telephonyManager.endCall();
+                    if (telecommManager != null) {
+                        hungUp = telecommManager.endCall();
                     }
                     interceptPowerKeyDown(!interactive || hungUp);
                 } else {
@@ -4238,19 +4247,19 @@
                         interceptScreenshotChord();
                     }
 
-                    TelephonyManager telephonyManager = getTelephonyService();
+                    TelecommManager telecommManager = getTelecommService();
                     boolean hungUp = false;
-                    if (telephonyManager != null) {
-                        if (telephonyManager.isRinging()) {
+                    if (telecommManager != null) {
+                        if (telecommManager.isRinging()) {
                             // Pressing Power while there's a ringing incoming
                             // call should silence the ringer.
-                            telephonyManager.silenceRinger();
+                            telecommManager.silenceRinger();
                         } else if ((mIncallPowerBehavior
                                 & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
-                                && telephonyManager.isOffhook() && interactive) {
+                                && telecommManager.isInAPhoneCall() && interactive) {
                             // Otherwise, if "Power button ends call" is enabled,
                             // the Power button will hang up any current active call.
-                            hungUp = telephonyManager.endCall();
+                            hungUp = telecommManager.endCall();
                         }
                     }
                     interceptPowerKeyDown(!interactive || hungUp
@@ -4286,9 +4295,9 @@
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                 if (down) {
-                    TelephonyManager telephonyManager = getTelephonyService();
-                    if (telephonyManager != null) {
-                        if (!telephonyManager.isIdle()) {
+                    TelecommManager telecommManager = getTelecommService();
+                    if (telecommManager != null) {
+                        if (telecommManager.isInAPhoneCall()) {
                             // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
                             // to avoid music playback.
                             break;
@@ -4321,12 +4330,12 @@
 
             case KeyEvent.KEYCODE_CALL: {
                 if (down) {
-                    TelephonyManager telephonyManager = getTelephonyService();
-                    if (telephonyManager != null) {
-                        if (telephonyManager.isRinging()) {
+                    TelecommManager telecommManager = getTelecommService();
+                    if (telecommManager != null) {
+                        if (telecommManager.isRinging()) {
                             Log.i(TAG, "interceptKeyBeforeQueueing:"
                                   + " CALL key-down while ringing: Answer the call!");
-                            telephonyManager.answerRingingCall();
+                            telecommManager.acceptRingingCall();
 
                             // And *don't* pass this key thru to the current activity
                             // (which is presumably the InCallScreen.)
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index bfa0402..14a462e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -29,8 +29,11 @@
 import java.util.List;
 import java.util.Map;
 
+import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.media.AudioService;
@@ -102,10 +105,12 @@
     public final static class Ops extends SparseArray<Op> {
         public final String packageName;
         public final int uid;
+        public final boolean isPrivileged;
 
-        public Ops(String _packageName, int _uid) {
+        public Ops(String _packageName, int _uid, boolean _isPrivileged) {
             packageName = _packageName;
             uid = _uid;
+            isPrivileged = _isPrivileged;
         }
     }
 
@@ -368,7 +373,11 @@
 
     @Override
     public void setMode(int code, int uid, String packageName, int mode) {
-        verifyIncomingUid(uid);
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return;
+        }
+        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), null);
         verifyIncomingOp(code);
         ArrayList<Callback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
@@ -556,7 +565,7 @@
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         synchronized (this) {
-            if (isOpRestricted(uid, code)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
@@ -642,7 +651,7 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
-            if (isOpRestricted(uid, code)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             if (op.duration == -1) {
@@ -679,7 +688,7 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
-            if (isOpRestricted(uid, code)) {
+            if (isOpRestricted(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -778,6 +787,7 @@
             if (!edit) {
                 return null;
             }
+            boolean isPrivileged = false;
             // This is the first time we have seen this package name under this uid,
             // so let's make sure it is valid.
             if (uid != 0) {
@@ -785,12 +795,19 @@
                 try {
                     int pkgUid = -1;
                     try {
-                        pkgUid = mContext.getPackageManager().getPackageUid(packageName,
-                                UserHandle.getUserId(uid));
-                    } catch (NameNotFoundException e) {
-                        if ("media".equals(packageName)) {
-                            pkgUid = Process.MEDIA_UID;
+                        ApplicationInfo appInfo = ActivityThread.getPackageManager()
+                                .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid));
+                        if (appInfo != null) {
+                            pkgUid = appInfo.uid;
+                            isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+                        } else {
+                            if ("media".equals(packageName)) {
+                                pkgUid = Process.MEDIA_UID;
+                                isPrivileged = false;
+                            }
                         }
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Could not contact PackageManager", e);
                     }
                     if (pkgUid != uid) {
                         // Oops!  The package name is not valid for the uid they are calling
@@ -803,7 +820,7 @@
                     Binder.restoreCallingIdentity(ident);
                 }
             }
-            ops = new Ops(packageName, uid);
+            ops = new Ops(packageName, uid, isPrivileged);
             pkgOps.put(packageName, ops);
         }
         return ops;
@@ -847,10 +864,18 @@
         return op;
     }
 
-    private boolean isOpRestricted(int uid, int code) {
+    private boolean isOpRestricted(int uid, int code, String packageName) {
         int userHandle = UserHandle.getUserId(uid);
         boolean[] opRestrictions = mOpRestrictions.get(userHandle);
         if ((opRestrictions != null) && opRestrictions[code]) {
+            if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+                synchronized (this) {
+                    Ops ops = getOpsLocked(uid, packageName, true);
+                    if ((ops != null) && ops.isPrivileged) {
+                        return false;
+                    }
+                }
+            }
             if (userHandle == UserHandle.USER_OWNER) {
                 if (uid != mDeviceOwnerUid) {
                     return true;
@@ -955,6 +980,27 @@
     void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
             XmlPullParserException, IOException {
         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        String isPrivilegedString = parser.getAttributeValue(null, "p");
+        boolean isPrivileged = false;
+        if (isPrivilegedString == null) {
+            try {
+                IPackageManager packageManager = ActivityThread.getPackageManager();
+                if (packageManager != null) {
+                    ApplicationInfo appInfo = ActivityThread.getPackageManager()
+                            .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
+                    if (appInfo != null) {
+                        isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+                    }
+                } else {
+                    // Could not load data, don't add to cache so it will be loaded later.
+                    return;
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not contact PackageManager", e);
+            }
+        } else {
+            isPrivileged = Boolean.parseBoolean(isPrivilegedString);
+        }
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -989,7 +1035,7 @@
                 }
                 Ops ops = pkgOps.get(pkgName);
                 if (ops == null) {
-                    ops = new Ops(pkgName, uid);
+                    ops = new Ops(pkgName, uid, isPrivileged);
                     pkgOps.put(pkgName, ops);
                 }
                 ops.put(op.op, op);
@@ -1033,6 +1079,16 @@
                         }
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
+                        synchronized (this) {
+                            Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
+                            // Should always be present as the list of PackageOps is generated
+                            // from Ops.
+                            if (ops != null) {
+                                out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
+                            } else {
+                                out.attribute(null, "p", Boolean.toString(false));
+                            }
+                        }
                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
                         for (int j=0; j<ops.size(); j++) {
                             AppOpsManager.OpEntry op = ops.get(j);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7fe1e82..64db8cf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -331,7 +331,9 @@
 
     /**
      * used internally to clear a wakelock when transitioning
-     * from one net to another
+     * from one net to another.  Clear happens when we get a new
+     * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
+     * after a timeout if no network is found (typically 1 min).
      */
     private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
 
@@ -420,6 +422,15 @@
      */
     private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
 
+    /**
+     * used internally to expire a wakelock when transitioning
+     * from one net to another.  Expire happens when we fail to find
+     * a new network (typically after 1 minute) -
+     * EVENT_CLEAR_NET_TRANSITION_WAKELOCK happens if we had found
+     * a replacement network.
+     */
+    private static final int EVENT_EXPIRE_NET_TRANSITION_WAKELOCK = 24;
+
 
     /** Handler used for internal events. */
     final private InternalHandler mHandler;
@@ -3290,6 +3301,7 @@
             if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
                 removeDataActivityTracking(nai);
                 mActiveDefaultNetwork = ConnectivityManager.TYPE_NONE;
+                requestNetworkTransitionWakelock(nai.name());
             }
             for (NetworkAgentInfo networkToActivate : toActivate) {
                 networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
@@ -3391,6 +3403,7 @@
         public void handleMessage(Message msg) {
             NetworkInfo info;
             switch (msg.what) {
+                case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
                 case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
                     String causedBy = null;
                     synchronized (ConnectivityService.this) {
@@ -3398,10 +3411,15 @@
                                 mNetTransitionWakeLock.isHeld()) {
                             mNetTransitionWakeLock.release();
                             causedBy = mNetTransitionWakeLockCausedBy;
+                        } else {
+                            break;
                         }
                     }
-                    if (causedBy != null) {
-                        log("NetTransition Wakelock for " + causedBy + " released by timeout");
+                    if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
+                        log("Failed to find a new network - expiring NetTransition Wakelock");
+                    } else {
+                        log("NetTransition Wakelock (" + (causedBy == null ? "unknown" : causedBy) +
+                                " cleared because we found a replacement network");
                     }
                     break;
                 }
@@ -3602,15 +3620,15 @@
     // restarted by subsequent callers.
     public void requestNetworkTransitionWakelock(String forWhom) {
         enforceConnectivityInternalPermission();
+        int serialNum = 0;
         synchronized (this) {
             if (mNetTransitionWakeLock.isHeld()) return;
-            mNetTransitionWakeLockSerialNumber++;
+            serialNum = ++mNetTransitionWakeLockSerialNumber;
             mNetTransitionWakeLock.acquire();
             mNetTransitionWakeLockCausedBy = forWhom;
         }
         mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
-                mNetTransitionWakeLockSerialNumber, 0),
+                EVENT_EXPIRE_NET_TRANSITION_WAKELOCK, serialNum, 0),
                 mNetTransitionWakeLockTimeout);
         return;
     }
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 1b71518..538501b 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -18,10 +18,12 @@
 
 import android.Manifest.permission;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.net.INetworkScoreCache;
 import android.net.INetworkScoreService;
+import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppManager;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.net.ScoredNetwork;
@@ -133,7 +135,13 @@
         // as scores should never be compared across apps; in practice, Settings should only be
         // allowing valid apps to be set as scorers, so failure here should be rare.
         clearInternal();
-        return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+        boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+        if (result) {
+            Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
+            intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
+            mContext.sendBroadcast(intent);
+        }
+        return result;
     }
 
     /** Clear scores. Callers are responsible for checking permissions as appropriate. */
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
new file mode 100644
index 0000000..fdcb3b9
--- /dev/null
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.pm.FeatureInfo;
+import android.os.*;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import static com.android.internal.util.ArrayUtils.appendInt;
+
+/**
+ * Loads global system configuration info.
+ */
+public class SystemConfig {
+    static final String TAG = "SystemConfig";
+
+    static SystemConfig sInstance;
+
+    // Group-ids that are given to all packages as read from etc/permissions/*.xml.
+    int[] mGlobalGids;
+
+    // These are the built-in uid -> permission mappings that were read from the
+    // system configuration files.
+    final SparseArray<HashSet<String>> mSystemPermissions =
+            new SparseArray<HashSet<String>>();
+
+    // These are the built-in shared libraries that were read from the
+    // system configuration files.  Keys are the library names; strings are the
+    // paths to the libraries.
+    final ArrayMap<String, String> mSharedLibraries
+            = new ArrayMap<String, String>();
+
+    // These are the features this devices supports that were read from the
+    // system configuration files.
+    final HashMap<String, FeatureInfo> mAvailableFeatures =
+            new HashMap<String, FeatureInfo>();
+
+    public static final class PermissionEntry {
+        public final String name;
+        public int[] gids;
+
+        PermissionEntry(String _name) {
+            name = _name;
+        }
+    }
+
+    // These are the permission -> gid mappings that were read from the
+    // system configuration files.
+    final ArrayMap<String, PermissionEntry> mPermissions =
+            new ArrayMap<String, PermissionEntry>();
+
+    // These are the packages that are white-listed to be able to run in the
+    // background while in power save mode, as read from the configuration files.
+    final ArraySet<String> mAllowInPowerSave = new ArraySet<String>();
+
+    public static SystemConfig getInstance() {
+        synchronized (SystemConfig.class) {
+            if (sInstance == null) {
+                sInstance = new SystemConfig();
+            }
+            return sInstance;
+        }
+    }
+
+    public int[] getGlobalGids() {
+        return mGlobalGids;
+    }
+
+    public SparseArray<HashSet<String>> getSystemPermissions() {
+        return mSystemPermissions;
+    }
+
+    public ArrayMap<String, String> getSharedLibraries() {
+        return mSharedLibraries;
+    }
+
+    public HashMap<String, FeatureInfo> getAvailableFeatures() {
+        return mAvailableFeatures;
+    }
+
+    public ArrayMap<String, PermissionEntry> getPermissions() {
+        return mPermissions;
+    }
+
+    public ArraySet<String> getAllowInPowerSave() {
+        return mAllowInPowerSave;
+    }
+
+    SystemConfig() {
+        // Read configuration from system
+        readPermissions(Environment.buildPath(
+                Environment.getRootDirectory(), "etc", "sysconfig"), false);
+        // Read configuration from the old permissions dir
+        readPermissions(Environment.buildPath(
+                Environment.getRootDirectory(), "etc", "permissions"), false);
+        // Only read features from OEM config
+        readPermissions(Environment.buildPath(
+                Environment.getOemDirectory(), "etc", "sysconfig"), true);
+        readPermissions(Environment.buildPath(
+                Environment.getOemDirectory(), "etc", "permissions"), true);
+    }
+
+    void readPermissions(File libraryDir, boolean onlyFeatures) {
+        // Read permissions from given directory.
+        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+            if (!onlyFeatures) {
+                Slog.w(TAG, "No directory " + libraryDir + ", skipping");
+            }
+            return;
+        }
+        if (!libraryDir.canRead()) {
+            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
+            return;
+        }
+
+        // Iterate over the files in the directory and scan .xml files
+        for (File f : libraryDir.listFiles()) {
+            // We'll read platform.xml last
+            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
+                continue;
+            }
+
+            if (!f.getPath().endsWith(".xml")) {
+                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
+                continue;
+            }
+            if (!f.canRead()) {
+                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
+                continue;
+            }
+
+            readPermissionsFromXml(f, onlyFeatures);
+        }
+
+        // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
+        final File permFile = new File(Environment.getRootDirectory(),
+                "etc/permissions/platform.xml");
+        readPermissionsFromXml(permFile, onlyFeatures);
+    }
+
+    private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
+        FileReader permReader = null;
+        try {
+            permReader = new FileReader(permFile);
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
+            return;
+        }
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(permReader);
+
+            int type;
+            while ((type=parser.next()) != parser.START_TAG
+                       && type != parser.END_DOCUMENT) {
+                ;
+            }
+
+            if (type != parser.START_TAG) {
+                throw new XmlPullParserException("No start tag found");
+            }
+
+            if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
+                throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+                        ", expected 'permissions' or 'config'");
+            }
+
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+                    break;
+                }
+
+                String name = parser.getName();
+                if ("group".equals(name) && !onlyFeatures) {
+                    String gidStr = parser.getAttributeValue(null, "gid");
+                    if (gidStr != null) {
+                        int gid = android.os.Process.getGidForName(gidStr);
+                        mGlobalGids = appendInt(mGlobalGids, gid);
+                    } else {
+                        Slog.w(TAG, "<group> without gid at "
+                                + parser.getPositionDescription());
+                    }
+
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else if ("permission".equals(name) && !onlyFeatures) {
+                    String perm = parser.getAttributeValue(null, "name");
+                    if (perm == null) {
+                        Slog.w(TAG, "<permission> without name at "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    perm = perm.intern();
+                    readPermission(parser, perm);
+
+                } else if ("assign-permission".equals(name) && !onlyFeatures) {
+                    String perm = parser.getAttributeValue(null, "name");
+                    if (perm == null) {
+                        Slog.w(TAG, "<assign-permission> without name at "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String uidStr = parser.getAttributeValue(null, "uid");
+                    if (uidStr == null) {
+                        Slog.w(TAG, "<assign-permission> without uid at "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    int uid = Process.getUidForName(uidStr);
+                    if (uid < 0) {
+                        Slog.w(TAG, "<assign-permission> with unknown uid \""
+                                + uidStr + "\" at "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    perm = perm.intern();
+                    HashSet<String> perms = mSystemPermissions.get(uid);
+                    if (perms == null) {
+                        perms = new HashSet<String>();
+                        mSystemPermissions.put(uid, perms);
+                    }
+                    perms.add(perm);
+                    XmlUtils.skipCurrentTag(parser);
+
+                } else if ("library".equals(name) && !onlyFeatures) {
+                    String lname = parser.getAttributeValue(null, "name");
+                    String lfile = parser.getAttributeValue(null, "file");
+                    if (lname == null) {
+                        Slog.w(TAG, "<library> without name at "
+                                + parser.getPositionDescription());
+                    } else if (lfile == null) {
+                        Slog.w(TAG, "<library> without file at "
+                                + parser.getPositionDescription());
+                    } else {
+                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
+                        mSharedLibraries.put(lname, lfile);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+
+                } else if ("feature".equals(name)) {
+                    String fname = parser.getAttributeValue(null, "name");
+                    if (fname == null) {
+                        Slog.w(TAG, "<feature> without name at "
+                                + parser.getPositionDescription());
+                    } else {
+                        //Log.i(TAG, "Got feature " + fname);
+                        FeatureInfo fi = new FeatureInfo();
+                        fi.name = fname;
+                        mAvailableFeatures.put(fname, fi);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+
+                } else if ("allow-in-power-save".equals(name)) {
+                    String pkgname = parser.getAttributeValue(null, "package");
+                    if (pkgname == null) {
+                        Slog.w(TAG, "<allow-in-power-save> without package at "
+                                + parser.getPositionDescription());
+                    } else {
+                        mAllowInPowerSave.add(pkgname);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+
+            }
+            permReader.close();
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Got execption parsing permissions.", e);
+        } catch (IOException e) {
+            Slog.w(TAG, "Got execption parsing permissions.", e);
+        }
+    }
+
+    void readPermission(XmlPullParser parser, String name)
+            throws IOException, XmlPullParserException {
+
+        name = name.intern();
+
+        PermissionEntry perm = mPermissions.get(name);
+        if (perm == null) {
+            perm = new PermissionEntry(name);
+            mPermissions.put(name, perm);
+        }
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                       || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if ("group".equals(tagName)) {
+                String gidStr = parser.getAttributeValue(null, "gid");
+                if (gidStr != null) {
+                    int gid = Process.getGidForName(gidStr);
+                    perm.gids = appendInt(perm.gids, gid);
+                } else {
+                    Slog.w(TAG, "<group> without gid at "
+                            + parser.getPositionDescription());
+                }
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 933046b..45367a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -36,9 +36,6 @@
 import android.app.IAppTask;
 import android.app.admin.DevicePolicyManager;
 import android.appwidget.AppWidgetManager;
-import android.content.DialogInterface.OnClickListener;
-import android.content.res.Resources;
-import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.os.BatteryStats;
 import android.os.PersistableBundle;
@@ -174,12 +171,8 @@
 import android.os.UpdateLock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.text.Spannable;
-import android.text.SpannableString;
 import android.text.format.DateUtils;
 import android.text.format.Time;
-import android.text.style.DynamicDrawableSpan;
-import android.text.style.ImageSpan;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -193,7 +186,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
-import android.widget.TextView;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -15028,7 +15020,6 @@
         int schedGroup;
         int procState;
         boolean foregroundActivities = false;
-        boolean interesting = false;
         BroadcastQueue queue;
         if (app == TOP_APP) {
             // The last app on the list is the foreground app.
@@ -15036,14 +15027,12 @@
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "top-activity";
             foregroundActivities = true;
-            interesting = true;
             procState = ActivityManager.PROCESS_STATE_TOP;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
-            interesting = true;
             procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
         } else if ((queue = isReceivingBroadcast(app)) != null) {
             // An app that is currently receiving a broadcast also
@@ -15159,10 +15148,6 @@
             }
         }
 
-        if (app.foregroundServices) {
-            interesting = true;
-        }
-
         if (app == mHeavyWeightProcess) {
             if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                 // We don't want to kill the current heavy-weight process.
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e1c7da9..1ecb43c 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1936,6 +1936,9 @@
                     resetTaskIfNeededLocked(r, r);
                     doShow = topRunningNonDelayedActivityLocked(null) == r;
                 }
+            } else if (options != null && new ActivityOptions(options).getAnimationType()
+                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
+                doShow = false;
             }
             if (SHOW_APP_STARTING_PREVIEW && doShow) {
                 // Figure out if we are transitioning from another activity that is
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3cbc6e2..5c5e3e3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -365,6 +365,20 @@
         }
     }
 
+    public void noteFlashlightOn() {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteFlashlightOnLocked();
+        }
+    }
+
+    public void noteFlashlightOff() {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteFlashlightOffLocked();
+        }
+    }
+
     public void noteWifiRunning(WorkSource ws) {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0bd7ef9..3ef9494 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -131,8 +131,8 @@
         taskId = _taskId;
         voiceSession = _voiceSession;
         voiceInteractor = _voiceInteractor;
-        setIntent(_intent, info);
         mActivities = new ArrayList<ActivityRecord>();
+        setIntent(_intent, info);
     }
 
     TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 4fbe845..49f2d6f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -318,9 +318,9 @@
      *
      * @param flag a key of option. For more details, look at
      *        {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
-     *        {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}
-     * @param value a value of option. Actual value varies flag. For more
-     *        details, look at description of flags
+     *        {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}.
+     * @param value a value of option. Actual value varies from flag to flag. For more
+     *        details, look at description of flags.
      */
     @ServiceThreadOnly
     void setOption(int flag, int value) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 4a0eb50..72d44fa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -78,6 +78,9 @@
     // This is not thread-safe. For external purpose use mSafeDeviceInfos.
     private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
 
+    // If true, TV going to standby mode puts other devices also to standby.
+    private boolean mAutoDeviceOff;
+
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiCec.DEVICE_TV);
         mPrevPortId = HdmiConstants.INVALID_PORT_ID;
@@ -496,6 +499,7 @@
                 new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback));
     }
 
+    // # Seq 25
     void setSystemAudioMode(boolean on) {
         synchronized (mLock) {
             if (on != mSystemAudioMode) {
@@ -975,4 +979,10 @@
             hotplugActions.get(0).pollAllDevicesNow();
         }
     }
+
+    @ServiceThreadOnly
+    void setAutoDeviceOff(boolean enabled) {
+        assertRunOnServiceThread();
+        mAutoDeviceOff = enabled;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 08413ec..c03bc99 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -724,7 +724,7 @@
         }
 
         @Override
-        public void setControlEnabled(boolean enabled) {
+        public void setControlEnabled(final boolean enabled) {
             enforceAccessPermission();
             synchronized (mLock) {
                 mHdmiControlEnabled = enabled;
@@ -741,6 +741,11 @@
                     if (tv == null) {
                         return;
                     }
+                    int value = enabled ? HdmiCec.ENABLED : HdmiCec.DISABLED;
+                    mCecController.setOption(HdmiCec.OPTION_CEC_ENABLE, value);
+                    if (mMhlController != null) {
+                        mMhlController.setOption(HdmiCec.OPTION_MHL_ENABLE, value);
+                    }
                     tv.routingAtEnableTime();
                 }
             });
@@ -760,6 +765,32 @@
                 }
             });
         }
+
+        @Override
+        public void setOption(final int key, final int value) {
+            if (!isTvDevice()) {
+                return;
+            }
+            switch (key) {
+                case HdmiCec.OPTION_CEC_AUTO_WAKEUP:
+                    mCecController.setOption(key, value);
+                    break;
+                case HdmiCec.OPTION_CEC_AUTO_DEVICE_OFF:
+                    // No need to pass this option to HAL.
+                    tv().setAutoDeviceOff(value == HdmiCec.ENABLED);
+                    break;
+                case HdmiCec.OPTION_MHL_INPUT_SWITCHING:  // Fall through
+                case HdmiCec.OPTION_MHL_POWER_CHARGE:
+                    if (mMhlController != null) {
+                        mMhlController.setOption(key, value);
+                    }
+                    break;
+            }
+        }
+
+        private boolean isTvDevice() {
+            return tv() != null;
+        }
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index 9d34589..f44c014 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -22,6 +22,7 @@
 /**
  * Feature action that handles System Audio initiated by AVR devices.
  */
+// # Seq 33
 final class SystemAudioActionFromAvr extends SystemAudioAction {
     /**
      * Constructor
@@ -58,7 +59,9 @@
             sendSystemAudioModeRequest();
             return;
         }
-        // TODO: Stop the action for System Audio Mode initialization if it is running.
+
+        removeAction(SystemAudioAutoInitiationAction.class);
+
         if (mTargetAudioStatus) {
             setSystemAudioMode(true);
             startAudioStatusAction();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 416a6b1..f0ec3bb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -39,6 +39,7 @@
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -74,6 +75,7 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
@@ -111,6 +113,7 @@
 import android.os.IPowerManager;
 import android.os.Message;
 import android.os.MessageQueue.IdleHandler;
+import android.os.PowerManagerInternal;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -119,6 +122,8 @@
 import android.telephony.TelephonyManager;
 import android.text.format.Formatter;
 import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.NtpTrustedTime;
@@ -133,9 +138,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
 import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -150,10 +155,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 
 import libcore.io.IoUtils;
@@ -224,8 +226,6 @@
 
     private static final int MSG_RULES_CHANGED = 1;
     private static final int MSG_METERED_IFACES_CHANGED = 2;
-    private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3;
-    private static final int MSG_PROCESS_DIED = 4;
     private static final int MSG_LIMIT_REACHED = 5;
     private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
     private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
@@ -240,41 +240,51 @@
 
     private IConnectivityManager mConnManager;
     private INotificationManager mNotifManager;
+    private PowerManagerInternal mPowerManagerInternal;
 
-    private final Object mRulesLock = new Object();
+    final Object mRulesLock = new Object();
 
-    private volatile boolean mScreenOn;
-    private volatile boolean mRestrictBackground;
+    volatile boolean mScreenOn;
+    volatile boolean mRestrictBackground;
+    volatile boolean mRestrictPower;
 
     private final boolean mSuppressDefaultPolicy;
 
     /** Defined network policies. */
-    private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap();
+    final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<
+            NetworkTemplate, NetworkPolicy>();
     /** Currently active network rules for ifaces. */
-    private HashMap<NetworkPolicy, String[]> mNetworkRules = Maps.newHashMap();
+    private final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<
+            NetworkPolicy, String[]>();
 
     /** Defined UID policies. */
-    private SparseIntArray mUidPolicy = new SparseIntArray();
+    final SparseIntArray mUidPolicy = new SparseIntArray();
     /** Currently derived rules for each UID. */
-    private SparseIntArray mUidRules = new SparseIntArray();
+    private final SparseIntArray mUidRules = new SparseIntArray();
+
+    /** UIDs that have been white-listed to always be able to have network access in
+     * power save mode. */
+    private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray();
 
     /** Set of ifaces that are metered. */
-    private HashSet<String> mMeteredIfaces = Sets.newHashSet();
+    private ArraySet<String> mMeteredIfaces = new ArraySet<String>();
     /** Set of over-limit templates that have been notified. */
-    private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
+    private final ArraySet<NetworkTemplate> mOverLimitNotified = new ArraySet<NetworkTemplate>();
 
     /** Set of currently active {@link Notification} tags. */
-    private HashSet<String> mActiveNotifs = Sets.newHashSet();
+    private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
 
     /** Foreground at both UID and PID granularity. */
-    private SparseBooleanArray mUidForeground = new SparseBooleanArray();
-    private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
-            SparseBooleanArray>();
+    private final SparseIntArray mUidState = new SparseIntArray();
+    final SparseArray<SparseIntArray> mUidPidState = new SparseArray<SparseIntArray>();
+
+    /** The current maximum process state that we are considering to be foreground. */
+    private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
 
     private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
             INetworkPolicyListener>();
 
-    private final Handler mHandler;
+    final Handler mHandler;
 
     private final AtomicFile mPolicyFile;
 
@@ -328,12 +338,42 @@
             return;
         }
 
+        final PackageManager pm = mContext.getPackageManager();
+
         synchronized (mRulesLock) {
+            SystemConfig sysConfig = SystemConfig.getInstance();
+            ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
+            for (int i=0; i<allowPower.size(); i++) {
+                String pkg = allowPower.valueAt(i);
+                try {
+                    ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+                    if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        mPowerSaveWhitelistAppIds.put(UserHandle.getAppId(ai.uid), true);
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                }
+            }
+
+            mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+            mPowerManagerInternal.registerLowPowerModeObserver(
+                    new PowerManagerInternal.LowPowerModeListener() {
+                @Override
+                public void onLowPowerModeChanged(boolean enabled) {
+                    synchronized (mRulesLock) {
+                        if (mRestrictPower != enabled) {
+                            mRestrictPower = enabled;
+                            updateRulesForGlobalChangeLocked(true);
+                        }
+                    }
+                }
+            });
+            mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled();
+
             // read policy from disk
             readPolicyLocked();
 
-            if (mRestrictBackground) {
-                updateRulesForRestrictBackgroundLocked();
+            if (mRestrictBackground || mRestrictPower) {
+                updateRulesForGlobalChangeLocked(true);
                 updateNotificationsLocked();
             }
         }
@@ -405,17 +445,38 @@
     private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
         @Override
         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
-            mHandler.obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED,
-                    pid, uid, foregroundActivities).sendToTarget();
         }
 
         @Override
         public void onProcessStateChanged(int pid, int uid, int procState) {
+            synchronized (mRulesLock) {
+                // because a uid can have multiple pids running inside, we need to
+                // remember all pid states and summarize foreground at uid level.
+
+                // record foreground for this specific pid
+                SparseIntArray pidState = mUidPidState.get(uid);
+                if (pidState == null) {
+                    pidState = new SparseIntArray(2);
+                    mUidPidState.put(uid, pidState);
+                }
+                pidState.put(pid, procState);
+                computeUidStateLocked(uid);
+            }
         }
 
         @Override
         public void onProcessDied(int pid, int uid) {
-            mHandler.obtainMessage(MSG_PROCESS_DIED, pid, uid).sendToTarget();
+            synchronized (mRulesLock) {
+                // clear records and recompute, when they exist
+                final SparseIntArray pidState = mUidPidState.get(uid);
+                if (pidState != null) {
+                    pidState.delete(pid);
+                    if (pidState.size() <= 0) {
+                        mUidPidState.remove(uid);
+                    }
+                    computeUidStateLocked(uid);
+                }
+            }
         }
     };
 
@@ -476,13 +537,12 @@
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
             if (userId == -1) return;
 
-            // Remove any policies for given user; both cleaning up after a
-            // USER_REMOVED, and one last sanity check during USER_ADDED
-            removePoliciesForUserLocked(userId);
-
-            // Update global restrict for new user
             synchronized (mRulesLock) {
-                updateRulesForRestrictBackgroundLocked();
+                // Remove any policies for given user; both cleaning up after a
+                // USER_REMOVED, and one last sanity check during USER_ADDED
+                removePoliciesForUserLocked(userId);
+                // Update global restrict for new user
+                updateRulesForGlobalChangeLocked(true);
             }
         }
     };
@@ -620,12 +680,11 @@
      * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
      * to show visible notifications as needed.
      */
-    private void updateNotificationsLocked() {
+    void updateNotificationsLocked() {
         if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
 
         // keep track of previously active notifications
-        final HashSet<String> beforeNotifs = Sets.newHashSet();
-        beforeNotifs.addAll(mActiveNotifs);
+        final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
         mActiveNotifs.clear();
 
         // TODO: when switching to kernel notifications, compute next future
@@ -633,7 +692,8 @@
 
         // examine stats for each active policy
         final long currentTime = currentTimeMillis();
-        for (NetworkPolicy policy : mNetworkPolicy.values()) {
+        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+            final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
             // ignore policies that aren't relevant to user
             if (!isTemplateRelevant(policy.template)) continue;
             if (!policy.hasCycle()) continue;
@@ -665,7 +725,8 @@
         }
 
         // cancel stale notifications that we didn't renew above
-        for (String tag : beforeNotifs) {
+        for (int i = beforeNotifs.size()-1; i >= 0; i--) {
+            final String tag = beforeNotifs.valueAt(i);
             if (!mActiveNotifs.contains(tag)) {
                 cancelNotification(tag);
             }
@@ -905,14 +966,15 @@
      * Proactively control network data connections when they exceed
      * {@link NetworkPolicy#limitBytes}.
      */
-    private void updateNetworkEnabledLocked() {
+    void updateNetworkEnabledLocked() {
         if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
 
         // TODO: reset any policy-disabled networks when any policy is removed
         // completely, which is currently rare case.
 
         final long currentTime = currentTimeMillis();
-        for (NetworkPolicy policy : mNetworkPolicy.values()) {
+        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+            final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
             // shortcut when policy has no limit
             if (policy.limitBytes == LIMIT_DISABLED || !policy.hasCycle()) {
                 setNetworkTemplateEnabled(policy.template, true);
@@ -967,7 +1029,7 @@
      * {@link NetworkPolicy} that need to be enforced. When matches found, set
      * remaining quota based on usage cycle and historical stats.
      */
-    private void updateNetworkRulesLocked() {
+    void updateNetworkRulesLocked() {
         if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
 
         final NetworkState[] states;
@@ -978,29 +1040,42 @@
             return;
         }
 
+        // If we are in restrict power mode, we want to treat all interfaces
+        // as metered, to restrict access to the network by uid.  However, we
+        // will not have a bandwidth limit.  Also only do this if restrict
+        // background data use is *not* enabled, since that takes precendence
+        // use over those networks can have a cost associated with it).
+        final boolean powerSave = mRestrictPower && !mRestrictBackground;
+
         // first, derive identity for all connected networks, which can be used
         // to match against templates.
-        final HashMap<NetworkIdentity, String> networks = Maps.newHashMap();
+        final ArrayMap<NetworkIdentity, String> networks = new ArrayMap<NetworkIdentity,
+                String>(states.length);
+        final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
         for (NetworkState state : states) {
             // stash identity and iface away for later use
             if (state.networkInfo.isConnected()) {
                 final String iface = state.linkProperties.getInterfaceName();
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
                 networks.put(ident, iface);
+                if (powerSave) {
+                    connIfaces.add(iface);
+                }
             }
         }
 
         // build list of rules and ifaces to enforce them against
         mNetworkRules.clear();
         final ArrayList<String> ifaceList = Lists.newArrayList();
-        for (NetworkPolicy policy : mNetworkPolicy.values()) {
+        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+            final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
 
             // collect all active ifaces that match this template
             ifaceList.clear();
-            for (Map.Entry<NetworkIdentity, String> entry : networks.entrySet()) {
-                final NetworkIdentity ident = entry.getKey();
+            for (int j = networks.size()-1; j >= 0; j--) {
+                final NetworkIdentity ident = networks.keyAt(j);
                 if (policy.template.matches(ident)) {
-                    final String iface = entry.getValue();
+                    final String iface = networks.valueAt(j);
                     ifaceList.add(iface);
                 }
             }
@@ -1012,13 +1087,14 @@
         }
 
         long lowestRule = Long.MAX_VALUE;
-        final HashSet<String> newMeteredIfaces = Sets.newHashSet();
+        final ArraySet<String> newMeteredIfaces = new ArraySet<String>(states.length);
 
         // apply each policy that we found ifaces for; compute remaining data
         // based on current cycle and historical stats, and push to kernel.
         final long currentTime = currentTimeMillis();
-        for (NetworkPolicy policy : mNetworkRules.keySet()) {
-            final String[] ifaces = mNetworkRules.get(policy);
+        for (int i = mNetworkRules.size()-1; i >= 0; i--) {
+            final NetworkPolicy policy = mNetworkRules.keyAt(i);
+            final String[] ifaces = mNetworkRules.valueAt(i);
 
             final long start;
             final long totalBytes;
@@ -1063,6 +1139,9 @@
                     removeInterfaceQuota(iface);
                     setInterfaceQuota(iface, quotaBytes);
                     newMeteredIfaces.add(iface);
+                    if (powerSave) {
+                        connIfaces.remove(iface);
+                    }
                 }
             }
 
@@ -1075,10 +1154,18 @@
             }
         }
 
+        for (int i = connIfaces.size()-1; i >= 0; i--) {
+            String iface = connIfaces.valueAt(i);
+            removeInterfaceQuota(iface);
+            setInterfaceQuota(iface, Long.MAX_VALUE);
+            newMeteredIfaces.add(iface);
+        }
+
         mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
 
         // remove quota on any trailing interfaces
-        for (String iface : mMeteredIfaces) {
+        for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
+            final String iface = mMeteredIfaces.valueAt(i);
             if (!newMeteredIfaces.contains(iface)) {
                 removeInterfaceQuota(iface);
             }
@@ -1108,9 +1195,10 @@
 
         // examine to see if any policy is defined for active mobile
         boolean mobileDefined = false;
-        for (NetworkPolicy policy : mNetworkPolicy.values()) {
-            if (policy.template.matches(probeIdent)) {
+        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+            if (mNetworkPolicy.valueAt(i).template.matches(probeIdent)) {
                 mobileDefined = true;
+                break;
             }
         }
 
@@ -1226,7 +1314,7 @@
                         final int policy = readIntAttribute(in, ATTR_POLICY);
 
                         if (UserHandle.isApp(uid)) {
-                            setUidPolicyUnchecked(uid, policy, false);
+                            setUidPolicyUncheckedLocked(uid, policy, false);
                         } else {
                             Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                         }
@@ -1237,7 +1325,7 @@
                         // TODO: set for other users during upgrade
                         final int uid = UserHandle.getUid(UserHandle.USER_OWNER, appId);
                         if (UserHandle.isApp(uid)) {
-                            setUidPolicyUnchecked(uid, policy, false);
+                            setUidPolicyUncheckedLocked(uid, policy, false);
                         } else {
                             Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                         }
@@ -1273,7 +1361,7 @@
         }
     }
 
-    private void writePolicyLocked() {
+    void writePolicyLocked() {
         if (LOGV) Slog.v(TAG, "writePolicyLocked()");
 
         FileOutputStream fos = null;
@@ -1289,7 +1377,8 @@
             writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
 
             // write all known network policies
-            for (NetworkPolicy policy : mNetworkPolicy.values()) {
+            for (int i = 0; i < mNetworkPolicy.size(); i++) {
+                final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
                 final NetworkTemplate template = policy.template;
 
                 out.startTag(null, TAG_NETWORK_POLICY);
@@ -1346,24 +1435,59 @@
             throw new IllegalArgumentException("cannot apply policy to UID " + uid);
         }
 
-        setUidPolicyUnchecked(uid, policy, true);
+        synchronized (mRulesLock) {
+            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+            if (oldPolicy != policy) {
+                setUidPolicyUncheckedLocked(uid, policy, true);
+            }
+        }
     }
 
-    private void setUidPolicyUnchecked(int uid, int policy, boolean persist) {
-        final int oldPolicy;
-        synchronized (mRulesLock) {
-            oldPolicy = getUidPolicy(uid);
-            mUidPolicy.put(uid, policy);
+    @Override
+    public void addUidPolicy(int uid, int policy) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
-            // uid policy changed, recompute rules and persist policy.
-            updateRulesForUidLocked(uid);
-            if (persist) {
-                writePolicyLocked();
+        if (!UserHandle.isApp(uid)) {
+            throw new IllegalArgumentException("cannot apply policy to UID " + uid);
+        }
+
+        synchronized (mRulesLock) {
+            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+            policy |= oldPolicy;
+            if (oldPolicy != policy) {
+                setUidPolicyUncheckedLocked(uid, policy, true);
             }
         }
     }
 
     @Override
+    public void removeUidPolicy(int uid, int policy) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        if (!UserHandle.isApp(uid)) {
+            throw new IllegalArgumentException("cannot apply policy to UID " + uid);
+        }
+
+        synchronized (mRulesLock) {
+            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+            policy = oldPolicy & ~policy;
+            if (oldPolicy != policy) {
+                setUidPolicyUncheckedLocked(uid, policy, true);
+            }
+        }
+    }
+
+    private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {
+        mUidPolicy.put(uid, policy);
+
+        // uid policy changed, recompute rules and persist policy.
+        updateRulesForUidLocked(uid);
+        if (persist) {
+            writePolicyLocked();
+        }
+    }
+
+    @Override
     public int getUidPolicy(int uid) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
@@ -1389,11 +1513,25 @@
         return uids;
     }
 
+    @Override
+    public int[] getPowerSaveAppIdWhitelist() {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mRulesLock) {
+            int size = mPowerSaveWhitelistAppIds.size();
+            int[] appids = new int[size];
+            for (int i = 0; i < size; i++) {
+                appids[i] = mPowerSaveWhitelistAppIds.keyAt(i);
+            }
+            return appids;
+        }
+    }
+
     /**
      * Remove any policies associated with given {@link UserHandle}, persisting
      * if any changes are made.
      */
-    private void removePoliciesForUserLocked(int userId) {
+    void removePoliciesForUserLocked(int userId) {
         if (LOGV) Slog.v(TAG, "removePoliciesForUserLocked()");
 
         int[] uids = new int[0];
@@ -1449,7 +1587,7 @@
         }
     }
 
-    private void addNetworkPolicyLocked(NetworkPolicy policy) {
+    void addNetworkPolicyLocked(NetworkPolicy policy) {
         mNetworkPolicy.put(policy.template, policy);
 
         updateNetworkEnabledLocked();
@@ -1480,7 +1618,7 @@
         }
     }
 
-    private void performSnooze(NetworkTemplate template, int type) {
+    void performSnooze(NetworkTemplate template, int type) {
         maybeRefreshTrustedTime();
         final long currentTime = currentTimeMillis();
         synchronized (mRulesLock) {
@@ -1515,7 +1653,7 @@
         maybeRefreshTrustedTime();
         synchronized (mRulesLock) {
             mRestrictBackground = restrictBackground;
-            updateRulesForRestrictBackgroundLocked();
+            updateRulesForGlobalChangeLocked(false);
             updateNotificationsLocked();
             writePolicyLocked();
         }
@@ -1534,7 +1672,8 @@
     }
 
     private NetworkPolicy findPolicyForNetworkLocked(NetworkIdentity ident) {
-        for (NetworkPolicy policy : mNetworkPolicy.values()) {
+        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+            NetworkPolicy policy = mNetworkPolicy.valueAt(i);
             if (policy.template.matches(ident)) {
                 return policy;
             }
@@ -1616,15 +1755,15 @@
 
         final IndentingPrintWriter fout = new IndentingPrintWriter(writer, "  ");
 
-        final HashSet<String> argSet = new HashSet<String>();
+        final ArraySet<String> argSet = new ArraySet<String>(args.length);
         for (String arg : args) {
             argSet.add(arg);
         }
 
         synchronized (mRulesLock) {
             if (argSet.contains("--unsnooze")) {
-                for (NetworkPolicy policy : mNetworkPolicy.values()) {
-                    policy.clearSnooze();
+                for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+                    mNetworkPolicy.valueAt(i).clearSnooze();
                 }
 
                 updateNetworkEnabledLocked();
@@ -1637,10 +1776,12 @@
             }
 
             fout.print("Restrict background: "); fout.println(mRestrictBackground);
+            fout.print("Restrict power: "); fout.println(mRestrictPower);
+            fout.print("Current foreground state: "); fout.println(mCurForegroundState);
             fout.println("Network policies:");
             fout.increaseIndent();
-            for (NetworkPolicy policy : mNetworkPolicy.values()) {
-                fout.println(policy.toString());
+            for (int i = 0; i < mNetworkPolicy.size(); i++) {
+                fout.println(mNetworkPolicy.valueAt(i).toString());
             }
             fout.decreaseIndent();
 
@@ -1658,8 +1799,22 @@
             }
             fout.decreaseIndent();
 
+            size = mPowerSaveWhitelistAppIds.size();
+            if (size > 0) {
+                fout.println("Power save whitelist app ids:");
+                fout.increaseIndent();
+                for (int i = 0; i < size; i++) {
+                    fout.print("UID=");
+                    fout.print(mPowerSaveWhitelistAppIds.keyAt(i));
+                    fout.print(": ");
+                    fout.print(mPowerSaveWhitelistAppIds.valueAt(i));
+                    fout.println();
+                }
+                fout.decreaseIndent();
+            }
+
             final SparseBooleanArray knownUids = new SparseBooleanArray();
-            collectKeys(mUidForeground, knownUids);
+            collectKeys(mUidState, knownUids);
             collectKeys(mUidRules, knownUids);
 
             fout.println("Status for known UIDs:");
@@ -1670,12 +1825,17 @@
                 fout.print("UID=");
                 fout.print(uid);
 
-                fout.print(" foreground=");
-                final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
+                int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+                fout.print(" state=");
+                fout.print(state);
+                fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
+
+                fout.print(" pids=");
+                final int foregroundIndex = mUidPidState.indexOfKey(uid);
                 if (foregroundIndex < 0) {
                     fout.print("UNKNOWN");
                 } else {
-                    dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
+                    dumpSparseIntArray(fout, mUidPidState.valueAt(foregroundIndex));
                 }
 
                 fout.print(" rules=");
@@ -1697,33 +1857,44 @@
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
         synchronized (mRulesLock) {
-            // only really in foreground when screen is also on
-            return mUidForeground.get(uid, false) && mScreenOn;
+            return isUidForegroundLocked(uid);
         }
     }
 
+    boolean isUidForegroundLocked(int uid) {
+        // only really in foreground when screen is also on
+        return mScreenOn && mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)
+                <= mCurForegroundState;
+    }
+
     /**
-     * Foreground for PID changed; recompute foreground at UID level. If
+     * Process state of PID changed; recompute state at UID level. If
      * changed, will trigger {@link #updateRulesForUidLocked(int)}.
      */
-    private void computeUidForegroundLocked(int uid) {
-        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+    void computeUidStateLocked(int uid) {
+        final SparseIntArray pidState = mUidPidState.get(uid);
 
         // current pid is dropping foreground; examine other pids
-        boolean uidForeground = false;
-        final int size = pidForeground.size();
-        for (int i = 0; i < size; i++) {
-            if (pidForeground.valueAt(i)) {
-                uidForeground = true;
-                break;
+        int uidState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+        if (pidState != null) {
+            final int size = pidState.size();
+            for (int i = 0; i < size; i++) {
+                final int state = pidState.valueAt(i);
+                if (state < uidState) {
+                    uidState = state;
+                }
             }
         }
 
-        final boolean oldUidForeground = mUidForeground.get(uid, false);
-        if (oldUidForeground != uidForeground) {
-            // foreground changed, push updated rules
-            mUidForeground.put(uid, uidForeground);
-            updateRulesForUidLocked(uid);
+        final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+        if (oldUidState != uidState) {
+            // state changed, push updated rules
+            mUidState.put(uid, uidState);
+            final boolean oldForeground = oldUidState <= mCurForegroundState;
+            final boolean newForeground = uidState <= mCurForegroundState;
+            if (oldForeground != newForeground) {
+                updateRulesForUidLocked(uid);
+            }
         }
     }
 
@@ -1743,22 +1914,30 @@
      */
     private void updateRulesForScreenLocked() {
         // only update rules for anyone with foreground activities
-        final int size = mUidForeground.size();
+        final int size = mUidState.size();
         for (int i = 0; i < size; i++) {
-            if (mUidForeground.valueAt(i)) {
-                final int uid = mUidForeground.keyAt(i);
+            if (mUidState.valueAt(i) <= mCurForegroundState) {
+                final int uid = mUidState.keyAt(i);
                 updateRulesForUidLocked(uid);
             }
         }
     }
 
     /**
-     * Update rules that might be changed by {@link #mRestrictBackground} value.
+     * Update rules that might be changed by {@link #mRestrictBackground}
+     * or {@link #mRestrictPower} value.
      */
-    private void updateRulesForRestrictBackgroundLocked() {
+    void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
         final PackageManager pm = mContext.getPackageManager();
         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
+        // If we are in restrict power mode, we allow all important apps
+        // to have data access.  Otherwise, we restrict data access to only
+        // the top apps.
+        mCurForegroundState = (!mRestrictBackground && mRestrictPower)
+                ? ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+                : ActivityManager.PROCESS_STATE_TOP;
+
         // update rules for all installed applications
         final List<UserInfo> users = um.getUsers();
         final List<ApplicationInfo> apps = pm.getInstalledApplications(
@@ -1774,6 +1953,11 @@
         // limit data usage for some internal system services
         updateRulesForUidLocked(android.os.Process.MEDIA_UID);
         updateRulesForUidLocked(android.os.Process.DRM_UID);
+
+        // If the set of restricted networks may have changed, re-evaluate those.
+        if (restrictedNetworksChanged) {
+            updateNetworkRulesLocked();
+        }
     }
 
     private static boolean isUidValidForRules(int uid) {
@@ -1786,21 +1970,31 @@
         return false;
     }
 
-    private void updateRulesForUidLocked(int uid) {
+    void updateRulesForUidLocked(int uid) {
         if (!isUidValidForRules(uid)) return;
 
-        final int uidPolicy = getUidPolicy(uid);
-        final boolean uidForeground = isUidForeground(uid);
+        final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
+        final boolean uidForeground = isUidForegroundLocked(uid);
 
         // derive active rules based on policy and active state
         int uidRules = RULE_ALLOW_ALL;
         if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
             // uid in background, and policy says to block metered data
             uidRules = RULE_REJECT_METERED;
-        }
-        if (!uidForeground && mRestrictBackground) {
-            // uid in background, and global background disabled
-            uidRules = RULE_REJECT_METERED;
+        } else if (mRestrictBackground) {
+            if (!uidForeground) {
+                // uid in background, and global background disabled
+                uidRules = RULE_REJECT_METERED;
+            }
+        } else if (mRestrictPower) {
+            final boolean whitelisted = mPowerSaveWhitelistAppIds.get(UserHandle.getAppId(uid));
+            if (!whitelisted && !uidForeground
+                    && (uidPolicy & POLICY_ALLOW_BACKGROUND_BATTERY_SAVE) == 0) {
+                // uid is in background, restrict power use mode is on (so we want to
+                // restrict all background network access), and this uid is not on the
+                // white list of those allowed background access.
+                uidRules = RULE_REJECT_METERED;
+            }
         }
 
         // TODO: only dispatch when rules actually change
@@ -1860,40 +2054,6 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
-                case MSG_FOREGROUND_ACTIVITIES_CHANGED: {
-                    final int pid = msg.arg1;
-                    final int uid = msg.arg2;
-                    final boolean foregroundActivities = (Boolean) msg.obj;
-
-                    synchronized (mRulesLock) {
-                        // because a uid can have multiple pids running inside, we need to
-                        // remember all pid states and summarize foreground at uid level.
-
-                        // record foreground for this specific pid
-                        SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-                        if (pidForeground == null) {
-                            pidForeground = new SparseBooleanArray(2);
-                            mUidPidForeground.put(uid, pidForeground);
-                        }
-                        pidForeground.put(pid, foregroundActivities);
-                        computeUidForegroundLocked(uid);
-                    }
-                    return true;
-                }
-                case MSG_PROCESS_DIED: {
-                    final int pid = msg.arg1;
-                    final int uid = msg.arg2;
-
-                    synchronized (mRulesLock) {
-                        // clear records and recompute, when they exist
-                        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-                        if (pidForeground != null) {
-                            pidForeground.delete(pid);
-                            computeUidForegroundLocked(uid);
-                        }
-                    }
-                    return true;
-                }
                 case MSG_LIMIT_REACHED: {
                     final String iface = (String) msg.obj;
 
@@ -2020,7 +2180,7 @@
     /**
      * Try refreshing {@link #mTime} when stale.
      */
-    private void maybeRefreshTrustedTime() {
+    void maybeRefreshTrustedTime() {
         if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
             mTime.forceRefresh();
         }
@@ -2070,14 +2230,7 @@
         }
     }
 
-    private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
-        final int size = source.size();
-        for (int i = 0; i < size; i++) {
-            target.put(source.keyAt(i), true);
-        }
-    }
-
-    private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
+    private static void dumpSparseIntArray(PrintWriter fout, SparseIntArray value) {
         fout.print("[");
         final int size = value.size();
         for (int i = 0; i < size; i++) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 89ab2ae..0658eee 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -51,6 +51,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
+import libcore.io.IoUtils;
 import libcore.io.Libcore;
 
 import java.io.File;
@@ -449,8 +450,9 @@
         }
 
         for (File file : files) {
-            final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(file);
+            NativeLibraryHelper.ApkHandle handle = null;
             try {
+                handle = NativeLibraryHelper.ApkHandle.create(file);
                 final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle,
                         Build.SUPPORTED_ABIS);
                 if (abiIndex >= 0) {
@@ -464,8 +466,11 @@
                     throw new InstallFailedException(abiIndex,
                             "Failed to copy native libraries for " + file);
                 }
+            } catch (IOException ioe) {
+                throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
+                        "Failed to create handle for " + file);
             } finally {
-                handle.close();
+                IoUtils.closeQuietly(handle);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cac27bc..9471cff 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -36,6 +36,7 @@
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.ArrayUtils.removeInt;
 
+import android.util.ArrayMap;
 import com.android.internal.R;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
@@ -50,6 +51,7 @@
 import com.android.server.IntentResolver;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -103,6 +105,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
+import android.content.pm.UserInfo;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
@@ -391,17 +394,20 @@
     final Settings mSettings;
     boolean mRestoredSettings;
 
-    // Group-ids that are given to all packages as read from etc/permissions/*.xml.
-    int[] mGlobalGids;
+    // System configuration read by SystemConfig.
+    final int[] mGlobalGids;
+    final SparseArray<HashSet<String>> mSystemPermissions;
+    final HashMap<String, FeatureInfo> mAvailableFeatures;
 
-    // These are the built-in uid -> permission mappings that were read from the
-    // etc/permissions.xml file.
-    final SparseArray<HashSet<String>> mSystemPermissions =
-            new SparseArray<HashSet<String>>();
+    // If mac_permissions.xml was found for seinfo labeling.
+    boolean mFoundPolicyFile;
 
-    static final class SharedLibraryEntry {
-        final String path;
-        final String apk;
+    // If a recursive restorecon of /data/data/<pkg> is needed.
+    private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+
+    public static final class SharedLibraryEntry {
+        public final String path;
+        public final String apk;
 
         SharedLibraryEntry(String _path, String _apk) {
             path = _path;
@@ -409,21 +415,9 @@
         }
     }
 
-    // These are the built-in shared libraries that were read from the
-    // etc/permissions.xml file.
-    final HashMap<String, SharedLibraryEntry> mSharedLibraries
-            = new HashMap<String, SharedLibraryEntry>();
-
-    // These are the features this devices supports that were read from the
-    // etc/permissions.xml file.
-    final HashMap<String, FeatureInfo> mAvailableFeatures =
-            new HashMap<String, FeatureInfo>();
-
-    // If mac_permissions.xml was found for seinfo labeling.
-    boolean mFoundPolicyFile;
-
-    // If a recursive restorecon of /data/data/<pkg> is needed.
-    private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+    // Currently known shared libraries.
+    final HashMap<String, SharedLibraryEntry> mSharedLibraries =
+            new HashMap<String, SharedLibraryEntry>();
 
     // All available activities, for your resolving pleasure.
     final ActivityIntentResolver mActivities =
@@ -467,6 +461,9 @@
 
     HashSet<PackageParser.Package> mDeferredDexOpt = null;
 
+    // Cache of users who need badging.
+    SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
+
     /** Token for keys in mPendingVerification. */
     private int mPendingVerificationToken = 0;
 
@@ -1331,6 +1328,11 @@
 
         getDefaultDisplayMetrics(context, mMetrics);
 
+        SystemConfig systemConfig = SystemConfig.getInstance();
+        mGlobalGids = systemConfig.getGlobalGids();
+        mSystemPermissions = systemConfig.getSystemPermissions();
+        mAvailableFeatures = systemConfig.getAvailableFeatures();
+
         synchronized (mInstallLock) {
         // writer
         synchronized (mPackages) {
@@ -1352,12 +1354,26 @@
             sUserManager = new UserManagerService(context, this,
                     mInstallLock, mPackages);
 
-            // Read permissions and features from system
-            readPermissions(Environment.buildPath(
-                    Environment.getRootDirectory(), "etc", "permissions"), false);
-            // Only read features from OEM
-            readPermissions(Environment.buildPath(
-                    Environment.getOemDirectory(), "etc", "permissions"), true);
+            // Propagate permission configuration in to package manager.
+            ArrayMap<String, SystemConfig.PermissionEntry> permConfig
+                    = systemConfig.getPermissions();
+            for (int i=0; i<permConfig.size(); i++) {
+                SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
+                BasePermission bp = mSettings.mPermissions.get(perm.name);
+                if (bp == null) {
+                    bp = new BasePermission(perm.name, null, BasePermission.TYPE_BUILTIN);
+                    mSettings.mPermissions.put(perm.name, bp);
+                }
+                if (perm.gids != null) {
+                    bp.gids = appendInts(bp.gids, perm.gids);
+                }
+            }
+
+            ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
+            for (int i=0; i<libConfig.size(); i++) {
+                mSharedLibraries.put(libConfig.keyAt(i),
+                        new SharedLibraryEntry(libConfig.valueAt(i), null));
+            }
 
             mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
 
@@ -1822,198 +1838,6 @@
         mSettings.removePackageLPw(ps.name);
     }
 
-    void readPermissions(File libraryDir, boolean onlyFeatures) {
-        // Read permissions from .../etc/permission directory.
-        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
-            Slog.w(TAG, "No directory " + libraryDir + ", skipping");
-            return;
-        }
-        if (!libraryDir.canRead()) {
-            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
-            return;
-        }
-
-        // Iterate over the files in the directory and scan .xml files
-        for (File f : libraryDir.listFiles()) {
-            // We'll read platform.xml last
-            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
-                continue;
-            }
-
-            if (!f.getPath().endsWith(".xml")) {
-                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
-                continue;
-            }
-            if (!f.canRead()) {
-                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
-                continue;
-            }
-
-            readPermissionsFromXml(f, onlyFeatures);
-        }
-
-        // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
-        final File permFile = new File(Environment.getRootDirectory(),
-                "etc/permissions/platform.xml");
-        readPermissionsFromXml(permFile, onlyFeatures);
-    }
-
-    private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
-        FileReader permReader = null;
-        try {
-            permReader = new FileReader(permFile);
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
-            return;
-        }
-
-        try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(permReader);
-
-            XmlUtils.beginDocument(parser, "permissions");
-
-            while (true) {
-                XmlUtils.nextElement(parser);
-                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
-                    break;
-                }
-
-                String name = parser.getName();
-                if ("group".equals(name) && !onlyFeatures) {
-                    String gidStr = parser.getAttributeValue(null, "gid");
-                    if (gidStr != null) {
-                        int gid = Process.getGidForName(gidStr);
-                        mGlobalGids = appendInt(mGlobalGids, gid);
-                    } else {
-                        Slog.w(TAG, "<group> without gid at "
-                                + parser.getPositionDescription());
-                    }
-
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-                } else if ("permission".equals(name) && !onlyFeatures) {
-                    String perm = parser.getAttributeValue(null, "name");
-                    if (perm == null) {
-                        Slog.w(TAG, "<permission> without name at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    perm = perm.intern();
-                    readPermission(parser, perm);
-
-                } else if ("assign-permission".equals(name) && !onlyFeatures) {
-                    String perm = parser.getAttributeValue(null, "name");
-                    if (perm == null) {
-                        Slog.w(TAG, "<assign-permission> without name at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    String uidStr = parser.getAttributeValue(null, "uid");
-                    if (uidStr == null) {
-                        Slog.w(TAG, "<assign-permission> without uid at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    int uid = Process.getUidForName(uidStr);
-                    if (uid < 0) {
-                        Slog.w(TAG, "<assign-permission> with unknown uid \""
-                                + uidStr + "\" at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    perm = perm.intern();
-                    HashSet<String> perms = mSystemPermissions.get(uid);
-                    if (perms == null) {
-                        perms = new HashSet<String>();
-                        mSystemPermissions.put(uid, perms);
-                    }
-                    perms.add(perm);
-                    XmlUtils.skipCurrentTag(parser);
-
-                } else if ("library".equals(name) && !onlyFeatures) {
-                    String lname = parser.getAttributeValue(null, "name");
-                    String lfile = parser.getAttributeValue(null, "file");
-                    if (lname == null) {
-                        Slog.w(TAG, "<library> without name at "
-                                + parser.getPositionDescription());
-                    } else if (lfile == null) {
-                        Slog.w(TAG, "<library> without file at "
-                                + parser.getPositionDescription());
-                    } else {
-                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
-                        mSharedLibraries.put(lname, new SharedLibraryEntry(lfile, null));
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("feature".equals(name)) {
-                    String fname = parser.getAttributeValue(null, "name");
-                    if (fname == null) {
-                        Slog.w(TAG, "<feature> without name at "
-                                + parser.getPositionDescription());
-                    } else {
-                        //Log.i(TAG, "Got feature " + fname);
-                        FeatureInfo fi = new FeatureInfo();
-                        fi.name = fname;
-                        mAvailableFeatures.put(fname, fi);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else {
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-                }
-
-            }
-            permReader.close();
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "Got execption parsing permissions.", e);
-        } catch (IOException e) {
-            Slog.w(TAG, "Got execption parsing permissions.", e);
-        }
-    }
-
-    void readPermission(XmlPullParser parser, String name)
-            throws IOException, XmlPullParserException {
-
-        name = name.intern();
-
-        BasePermission bp = mSettings.mPermissions.get(name);
-        if (bp == null) {
-            bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN);
-            mSettings.mPermissions.put(name, bp);
-        }
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-               && (type != XmlPullParser.END_TAG
-                       || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG
-                    || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            if ("group".equals(tagName)) {
-                String gidStr = parser.getAttributeValue(null, "gid");
-                if (gidStr != null) {
-                    int gid = Process.getGidForName(gidStr);
-                    bp.gids = appendInt(bp.gids, gid);
-                } else {
-                    Slog.w(TAG, "<group> without gid at "
-                            + parser.getPositionDescription());
-                }
-            }
-            XmlUtils.skipCurrentTag(parser);
-        }
-    }
-
     static int[] appendInts(int[] cur, int[] add) {
         if (add == null) return cur;
         if (cur == null) return add;
@@ -3591,7 +3415,6 @@
         String className;
         if (targetUserId == UserHandle.USER_OWNER) {
             className = FORWARD_INTENT_TO_USER_OWNER;
-            forwardingResolveInfo.showTargetUserIcon = true;
         } else {
             className = FORWARD_INTENT_TO_MANAGED_PROFILE;
         }
@@ -3599,6 +3422,10 @@
                 mAndroidApplication.packageName, className);
         ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
                 sourceUserId);
+        if (targetUserId == UserHandle.USER_OWNER) {
+            forwardingActivityInfo.showUserIcon = UserHandle.USER_OWNER;
+            forwardingResolveInfo.noResourceId = true;
+        }
         forwardingResolveInfo.activityInfo = forwardingActivityInfo;
         forwardingResolveInfo.priority = 0;
         forwardingResolveInfo.preferredOrder = 0;
@@ -5512,8 +5339,9 @@
          */
         if (pkg.applicationInfo.nativeLibraryDir != null) {
             // TODO: extend to extract native code from split APKs
-            final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
+            ApkHandle handle = null;
             try {
+                handle = ApkHandle.create(scanFile.getPath());
                 // Enable gross and lame hacks for apps that are built with old
                 // SDK tools. We must scan their APKs for renderscript bitcode and
                 // not launch them if it's present. Don't bother checking on devices
@@ -5628,7 +5456,7 @@
             } catch (IOException ioe) {
                 Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
             } finally {
-                handle.close();
+                IoUtils.closeQuietly(handle);
             }
         }
 
@@ -7027,7 +6855,11 @@
             res.isDefault = info.hasDefault;
             res.labelRes = info.labelRes;
             res.nonLocalizedLabel = info.nonLocalizedLabel;
-            res.icon = info.icon;
+            if (userNeedsBadging(userId)) {
+                res.noResourceId = true;
+            } else {
+                res.icon = info.icon;
+            }
             res.system = isSystemApp(res.activityInfo.applicationInfo);
             return res;
         }
@@ -8219,17 +8051,22 @@
      *
      * @return true if verification should be performed
      */
-    private boolean isVerificationEnabled(int flags) {
+    private boolean isVerificationEnabled(int userId, int flags) {
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
 
+        boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
+
         // Check if installing from ADB
         if ((flags & PackageManager.INSTALL_FROM_ADB) != 0) {
             // Do not run verification in a test harness environment
             if (ActivityManager.isRunningInTestHarness()) {
                 return false;
             }
+            if (ensureVerifyAppsEnabled) {
+                return true;
+            }
             // Check if the developer does not want package verification for ADB installs
             if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                     android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {
@@ -8237,6 +8074,10 @@
             }
         }
 
+        if (ensureVerifyAppsEnabled) {
+            return true;
+        }
+
         return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                 android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
     }
@@ -8807,7 +8648,7 @@
                  */
                 final int requiredUid = mRequiredVerifierPackage == null ? -1
                         : getPackageUid(mRequiredVerifierPackage, userIdentifier);
-                if (requiredUid != -1 && isVerificationEnabled(flags)) {
+                if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) {
                     final Intent verification = new Intent(
                             Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                     verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
@@ -9340,10 +9181,11 @@
                 nativeLibraryFile.delete();
             }
 
-            final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile);
             String[] abiList = (abiOverride != null) ?
                     new String[] { abiOverride } : Build.SUPPORTED_ABIS;
+            ApkHandle handle = null;
             try {
+                handle = ApkHandle.create(codeFile);
                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
                         abiOverride == null &&
                         NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
@@ -9358,7 +9200,7 @@
                 Slog.e(TAG, "Copying native libraries failed", e);
                 ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
             } finally {
-                handle.close();
+                IoUtils.closeQuietly(handle);
             }
 
             return ret;
@@ -13021,23 +12863,30 @@
                                     final File newNativeDir = new File(newNativePath);
 
                                     if (!isForwardLocked(pkg) && !isExternal(pkg)) {
-                                        // NOTE: We do not report any errors from the APK scan and library
-                                        // copy at this point.
-                                        NativeLibraryHelper.ApkHandle handle =
-                                                new NativeLibraryHelper.ApkHandle(newCodePath);
-                                        final int abi = NativeLibraryHelper.findSupportedAbi(
-                                                handle, Build.SUPPORTED_ABIS);
-                                        if (abi >= 0) {
-                                            NativeLibraryHelper.copyNativeBinariesIfNeededLI(
-                                                    handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+                                        ApkHandle handle = null;
+                                        try {
+                                            handle = ApkHandle.create(newCodePath);
+                                            final int abi = NativeLibraryHelper.findSupportedAbi(
+                                                    handle, Build.SUPPORTED_ABIS);
+                                            if (abi >= 0) {
+                                                NativeLibraryHelper.copyNativeBinariesIfNeededLI(
+                                                        handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+                                            }
+                                        } catch (IOException ioe) {
+                                            Slog.w(TAG, "Unable to extract native libs for package :"
+                                                    + mp.packageName, ioe);
+                                            returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+                                        } finally {
+                                            IoUtils.closeQuietly(handle);
                                         }
-                                        handle.close();
                                     }
                                     final int[] users = sUserManager.getUserIds();
-                                    for (int user : users) {
-                                        if (mInstaller.linkNativeLibraryDirectory(pkg.packageName,
-                                                newNativePath, user) < 0) {
-                                            returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+                                    if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+                                        for (int user : users) {
+                                            if (mInstaller.linkNativeLibraryDirectory(pkg.packageName,
+                                                    newNativePath, user) < 0) {
+                                                returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+                                            }
                                         }
                                     }
 
@@ -13155,6 +13004,7 @@
             // other disk I/O going on, that we'll let it slide for now.
             mInstaller.removeUserDataDirs(userHandle);
         }
+        mUserNeedsBadging.delete(userHandle);
     }
 
     /** Called by UserManagerService */
@@ -13229,4 +13079,26 @@
     public IPackageInstaller getPackageInstaller() {
         return mInstallerService;
     }
+
+    private boolean userNeedsBadging(int userId) {
+        int index = mUserNeedsBadging.indexOfKey(userId);
+        if (index < 0) {
+            final UserInfo userInfo;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                userInfo = sUserManager.getUserInfo(userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            final boolean b;
+            if (userInfo != null && userInfo.isManagedProfile()) {
+                b = true;
+            } else {
+                b = false;
+            }
+            mUserNeedsBadging.put(userId, b);
+            return b;
+        }
+        return mUserNeedsBadging.valueAt(index);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c78249b..81302b9 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -346,31 +346,21 @@
      */
     public static boolean assignSeinfoValue(PackageParser.Package pkg) {
 
-        /*
-         * Non system installed apps should be treated the same. This
-         * means that any post-loaded apk will be assigned the default
-         * tag, if one exists in the policy, else null, without respect
-         * to the signing key.
-         */
-        if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
-            ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
+        // We just want one of the signatures to match.
+        for (Signature s : pkg.mSignatures) {
+            if (s == null)
+                continue;
 
-            // We just want one of the signatures to match.
-            for (Signature s : pkg.mSignatures) {
-                if (s == null)
-                    continue;
+            Policy policy = sSigSeinfo.get(s);
+            if (policy != null) {
+                String seinfo = policy.checkPolicy(pkg.packageName);
+                if (seinfo != null) {
+                    pkg.applicationInfo.seinfo = seinfo;
+                    if (DEBUG_POLICY_INSTALL)
+                        Slog.i(TAG, "package (" + pkg.packageName +
+                               ") labeled with seinfo=" + seinfo);
 
-                Policy policy = sSigSeinfo.get(s);
-                if (policy != null) {
-                    String seinfo = policy.checkPolicy(pkg.packageName);
-                    if (seinfo != null) {
-                        pkg.applicationInfo.seinfo = seinfo;
-                        if (DEBUG_POLICY_INSTALL)
-                            Slog.i(TAG, "package (" + pkg.packageName +
-                                   ") labeled with seinfo=" + seinfo);
-
-                        return true;
-                    }
+                    return true;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 544e93b..194e85d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -838,6 +838,7 @@
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
+        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
         serializer.endTag(null, TAG_RESTRICTIONS);
     }
 
@@ -980,6 +981,7 @@
         readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
         readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
+        readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
     }
 
     private void readBoolean(XmlPullParser parser, Bundle restrictions,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b4cf2ae..f2703ad 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -189,13 +189,14 @@
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
+            Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
+            Surface outSurface) {
         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outConfig, outSurface);
+                outStableInsets, outConfig, outSurface);
         if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
         return res;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1d85723..771b53b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2835,7 +2835,8 @@
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
+            Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
+            Surface outSurface) {
         boolean toBeDisplayed = false;
         boolean inTouchMode;
         boolean configChanged;
@@ -3112,6 +3113,7 @@
             outOverscanInsets.set(win.mOverscanInsets);
             outContentInsets.set(win.mContentInsets);
             outVisibleInsets.set(win.mVisibleInsets);
+            outStableInsets.set(win.mStableInsets);
             if (localLOGV) Slog.v(
                 TAG, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
@@ -8878,6 +8880,8 @@
                             + " " + w.mContentInsets.toShortString()
                             + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
                             + " " + w.mVisibleInsets.toShortString()
+                            + " stableInsetsChanged=" + w.mStableInsetsChanged
+                            + " " + w.mStableInsets.toShortString()
                             + " surfaceResized=" + winAnimator.mSurfaceResized
                             + " configChanged=" + configChanged);
                 }
@@ -8885,6 +8889,7 @@
                 w.mLastOverscanInsets.set(w.mOverscanInsets);
                 w.mLastContentInsets.set(w.mContentInsets);
                 w.mLastVisibleInsets.set(w.mVisibleInsets);
+                w.mLastStableInsets.set(w.mStableInsets);
                 makeWindowFreezingScreenIfNeededLocked(w);
                 // If the orientation is changing, then we need to
                 // hold off on unfreezing the display until this
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7003c8c..23ab73c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -167,6 +167,14 @@
     boolean mOverscanInsetsChanged;
 
     /**
+     * Insets that determine the area covered by the stable system windows.  These are in the
+     * application's coordinate space (without compatibility scale applied).
+     */
+    final Rect mStableInsets = new Rect();
+    final Rect mLastStableInsets = new Rect();
+    boolean mStableInsetsChanged;
+
+    /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
      */
@@ -225,6 +233,7 @@
     final Rect mParentFrame = new Rect();
     final Rect mVisibleFrame = new Rect();
     final Rect mDecorFrame = new Rect();
+    final Rect mStableFrame = new Rect();
 
     boolean mContentChanged;
 
@@ -470,7 +479,7 @@
     }
 
     @Override
-    public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf) {
+    public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf) {
         mHaveFrame = true;
 
         TaskStack stack = mAppToken != null ? getStack() : null;
@@ -537,6 +546,7 @@
         mContentFrame.set(cf);
         mVisibleFrame.set(vf);
         mDecorFrame.set(dcf);
+        mStableFrame.set(sf);
 
         final int fw = mFrame.width();
         final int fh = mFrame.height();
@@ -574,6 +584,11 @@
                 Math.min(mVisibleFrame.right, mFrame.right),
                 Math.min(mVisibleFrame.bottom, mFrame.bottom));
 
+        mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
+                Math.max(mStableFrame.top, mFrame.top),
+                Math.min(mStableFrame.right, mFrame.right),
+                Math.min(mStableFrame.bottom, mFrame.bottom));
+
         mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0),
                 Math.max(mOverscanFrame.top - mFrame.top, 0),
                 Math.max(mFrame.right - mOverscanFrame.right, 0),
@@ -589,6 +604,11 @@
                 mFrame.right - mVisibleFrame.right,
                 mFrame.bottom - mVisibleFrame.bottom);
 
+        mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
+                Math.max(mStableFrame.top - mFrame.top, 0),
+                Math.max(mFrame.right - mStableFrame.right, 0),
+                Math.max(mFrame.bottom - mStableFrame.bottom, 0));
+
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -597,6 +617,7 @@
             mOverscanInsets.scale(mInvGlobalScale);
             mContentInsets.scale(mInvGlobalScale);
             mVisibleInsets.scale(mInvGlobalScale);
+            mStableInsets.scale(mInvGlobalScale);
 
             // Also the scaled frame that we report to the app needs to be
             // adjusted to be in its coordinate space.
@@ -618,7 +639,8 @@
                 + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
                 + "): frame=" + mFrame.toShortString()
                 + " ci=" + mContentInsets.toShortString()
-                + " vi=" + mVisibleInsets.toShortString());
+                + " vi=" + mVisibleInsets.toShortString()
+                + " vi=" + mStableInsets.toShortString());
     }
 
     @Override
@@ -724,6 +746,7 @@
         mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
         mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
         mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
+        mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
         return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged;
     }
 
@@ -1344,6 +1367,7 @@
             final Rect overscanInsets = mLastOverscanInsets;
             final Rect contentInsets = mLastContentInsets;
             final Rect visibleInsets = mLastVisibleInsets;
+            final Rect stableInsets = mLastStableInsets;
             final boolean reportDraw = mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING;
             final Configuration newConfig = configChanged ? mConfiguration : null;
             if (mClient instanceof IWindow.Stub) {
@@ -1353,15 +1377,15 @@
                     public void run() {
                         try {
                             mClient.resized(frame, overscanInsets, contentInsets,
-                                    visibleInsets, reportDraw, newConfig);
+                                    visibleInsets, stableInsets,  reportDraw, newConfig);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
                         }
                     }
                 });
             } else {
-                mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw,
-                        newConfig);
+                mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+                        reportDraw, newConfig);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -1373,6 +1397,7 @@
             mOverscanInsetsChanged = false;
             mContentInsetsChanged = false;
             mVisibleInsetsChanged = false;
+            mStableInsetsChanged = false;
             mWinAnimator.mSurfaceResized = false;
         } catch (RemoteException e) {
             mOrientationChanging = false;
@@ -1522,11 +1547,13 @@
                     mOverscanInsets.printShortString(pw);
                     pw.print(" content="); mContentInsets.printShortString(pw);
                     pw.print(" visible="); mVisibleInsets.printShortString(pw);
+                    pw.print(" stable="); mStableInsets.printShortString(pw);
                     pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
                     mLastOverscanInsets.printShortString(pw);
                     pw.print(" content="); mLastContentInsets.printShortString(pw);
                     pw.print(" visible="); mLastVisibleInsets.printShortString(pw);
+                    pw.print(" stable="); mLastStableInsets.printShortString(pw);
                     pw.println();
         }
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0d05c5f..52ca098 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -404,6 +404,9 @@
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
 
         try {
+            Slog.i(TAG, "Reading configuration...");
+            SystemConfig.getInstance();
+
             Slog.i(TAG, "Scheduling Policy");
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
 
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 39f228f..0575a5e9 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -119,7 +119,6 @@
                     final UserState userState;
                     synchronized (mLock) {
                         userState = getCurrentUserStateLocked();
-                        userState.updateIfNeededLocked();
                     }
                     // This is the first time we switch to this user after boot, so
                     // now is the time to remove obsolete print jobs since they
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index f647814..33edb11 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -126,7 +126,8 @@
         mSpooler = new RemotePrintSpooler(context, userId, this);
         mHandler = new UserStateHandler(context.getMainLooper());
         synchronized (mLock) {
-            enableSystemPrintServicesOnFirstBootLocked();
+            enableSystemPrintServicesLocked();
+            onConfigurationChangedLocked();
         }
     }
 
@@ -727,7 +728,7 @@
         }
     }
 
-    private void enableSystemPrintServicesOnFirstBootLocked() {
+    private void enableSystemPrintServicesLocked() {
         // Load enabled and installed services.
         readEnabledPrintServicesLocked();
         readInstalledPrintServicesLocked();
@@ -1159,6 +1160,7 @@
                 Log.w(LOG_TAG, "Not destroying - session destroyed");
                 return;
             }
+            mIsDestroyed = true;
             // Make sure printer tracking is stopped.
             final int printerCount = mStateTrackedPrinters.size();
             for (int i = 0; i < printerCount; i++) {
diff --git a/telecomm/java/android/telecomm/CallFeatures.java b/telecomm/java/android/telecomm/CallFeatures.java
new file mode 100644
index 0000000..076d25a
--- /dev/null
+++ b/telecomm/java/android/telecomm/CallFeatures.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecomm;
+
+/**
+ * Defines features of a call.  These features represent properties of a call for which it may be
+ * desirable to display an in-call indicator.
+ */
+public final class CallFeatures {
+    private CallFeatures() {};
+
+    public static final int NONE = 0x0;
+    public static final int VoLTE = 0x1;
+    public static final int VoWIFI = 0x2;
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index d2caca0..44380e2 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -408,4 +408,16 @@
     public abstract void splitFromConference(String callId);
 
     public void onPostDialContinue(String callId, boolean proceed) {}
+
+    public void onPostDialWait(Connection conn, String remaining) {}
+
+    /**
+     * Called when changes to the features of a call occurs. Features are defined in
+     * {@link android.telecomm.CallFeatures}.  The active features for the call are represented as
+     * bits in the features bit-mask.
+     *
+     * @param callId The call to set the features for.
+     * @param features The new features of the call.
+     */
+    public abstract void onFeaturesChanged(String callId, int features);
 }
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index 8412e80..1b3c0a3 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -354,4 +354,22 @@
             }
         }
     }
+
+    /**
+    * Set the features associated with the given call.
+    * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+    * bit-mask.
+    *
+    * @param callId The unique ID of the call to set features for.
+    * @param features The features.
+    */
+    public void setFeatures(String callId, int features) {
+        Log.v(this, "setFeatures: %d", features);
+        for (ICallServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.setFeatures(callId, features);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java
index 9e3c48d..76b28fa 100644
--- a/telecomm/java/android/telecomm/CallVideoClient.java
+++ b/telecomm/java/android/telecomm/CallVideoClient.java
@@ -17,6 +17,7 @@
 package android.telecomm;
 
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 
@@ -66,22 +67,22 @@
      */
     public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
 
-    private static final int MSG_ON_RECEIVE_SESSION_MODIFY_REQUEST = 1;
-    private static final int MSG_ON_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
-    private static final int MSG_ON_CALL_SESSION_EVENT = 3;
-    private static final int MSG_ON_UPDATED_PEER_DIMENSIONS = 4;
-    private static final int MSG_ON_UPDATE_CALL_DATA_USAGE = 5;
-    private static final int MSG_ON_CAMERA_CAPABILITIES_CHANGE = 6;
+    private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
+    private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
+    private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
+    private static final int MSG_UPDATE_PEER_DIMENSIONS = 4;
+    private static final int MSG_UPDATE_CALL_DATA_USAGE = 5;
+    private static final int MSG_HANDLE_CAMERA_CAPABILITIES_CHANGE = 6;
 
     /** Default Handler used to consolidate binder method calls onto a single thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_ON_RECEIVE_SESSION_MODIFY_REQUEST:
+                case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
                     onReceiveSessionModifyRequest((VideoCallProfile) msg.obj);
                     break;
-                case MSG_ON_RECEIVE_SESSION_MODIFY_RESPONSE: {
+                case MSG_RECEIVE_SESSION_MODIFY_RESPONSE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         int status = (int) args.arg1;
@@ -95,25 +96,25 @@
                     }
                     break;
                 }
-                case MSG_ON_CALL_SESSION_EVENT:
-                    onCallSessionEvent((int) msg.obj);
+                case MSG_HANDLE_CALL_SESSION_EVENT:
+                    onHandleCallSessionEvent((int) msg.obj);
                     break;
-                case MSG_ON_UPDATED_PEER_DIMENSIONS: {
+                case MSG_UPDATE_PEER_DIMENSIONS: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         int width = (int) args.arg1;
                         int height = (int) args.arg2;
-                        onUpdatedPeerDimensions(width, height);
+                        onUpdatePeerDimensions(width, height);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
-                case MSG_ON_UPDATE_CALL_DATA_USAGE:
+                case MSG_UPDATE_CALL_DATA_USAGE:
                     onUpdateCallDataUsage(msg.arg1);
                     break;
-                case MSG_ON_CAMERA_CAPABILITIES_CHANGE:
-                    onCameraCapabilitiesChange((CallCameraCapabilities) msg.obj);
+                case MSG_HANDLE_CAMERA_CAPABILITIES_CHANGE:
+                    onHandleCameraCapabilitiesChange((CallCameraCapabilities) msg.obj);
                     break;
                 default:
                     break;
@@ -126,42 +127,42 @@
      */
     private final class CallVideoClientBinder extends ICallVideoClient.Stub {
         @Override
-        public void onReceiveSessionModifyRequest(VideoCallProfile videoCallProfile) {
-            mHandler.obtainMessage(MSG_ON_RECEIVE_SESSION_MODIFY_REQUEST,
+        public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile) {
+            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
                     videoCallProfile).sendToTarget();
         }
 
         @Override
-        public void onReceiveSessionModifyResponse(int status,
+        public void receiveSessionModifyResponse(int status,
                 VideoCallProfile requestProfile, VideoCallProfile responseProfile) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = status;
             args.arg2 = requestProfile;
             args.arg3 = responseProfile;
-            mHandler.obtainMessage(MSG_ON_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+            mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
         }
 
         @Override
-        public void onCallSessionEvent(int event) {
-            mHandler.obtainMessage(MSG_ON_CALL_SESSION_EVENT, event).sendToTarget();
+        public void handleCallSessionEvent(int event) {
+            mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, event).sendToTarget();
         }
 
         @Override
-        public void onUpdatedPeerDimensions(int width, int height) {
+        public void updatePeerDimensions(int width, int height) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = width;
             args.arg2 = height;
-            mHandler.obtainMessage(MSG_ON_UPDATED_PEER_DIMENSIONS, args).sendToTarget();
+            mHandler.obtainMessage(MSG_UPDATE_PEER_DIMENSIONS, args).sendToTarget();
         }
 
         @Override
-        public void onUpdateCallDataUsage(int dataUsage) {
-            mHandler.obtainMessage(MSG_ON_UPDATE_CALL_DATA_USAGE, dataUsage).sendToTarget();
+        public void updateCallDataUsage(int dataUsage) {
+            mHandler.obtainMessage(MSG_UPDATE_CALL_DATA_USAGE, dataUsage).sendToTarget();
         }
 
         @Override
-        public void onCameraCapabilitiesChange(CallCameraCapabilities cameraCapabilities) {
-            mHandler.obtainMessage(MSG_ON_CAMERA_CAPABILITIES_CHANGE,
+        public void handleCameraCapabilitiesChange(CallCameraCapabilities cameraCapabilities) {
+            mHandler.obtainMessage(MSG_HANDLE_CAMERA_CAPABILITIES_CHANGE,
                     cameraCapabilities).sendToTarget();
         }
     }
@@ -173,21 +174,19 @@
     }
 
     /**
-     * Retrieves the binder.
-     *
-     * @return The binder.
+     * Returns binder object which can be used across IPC methods.
      * @hide
      */
-    public final CallVideoClientBinder getBinder() {
+    public final IBinder getBinder() {
         return mBinder;
     }
 
     /**
      * Called when a session modification request is received from the remote device.
-     * The remote request is sent via {@link CallVideoProvider#sendSessionModifyRequest}.
+     * The remote request is sent via {@link CallVideoProvider#onSendSessionModifyRequest}.
      * The InCall UI is responsible for potentially prompting the user whether they wish to accept
      * the new call profile (e.g. prompt user if they wish to accept an upgrade from an audio to a
-     * video call) and should call {@link CallVideoProvider#sendSessionModifyResponse} to indicate
+     * video call) and should call {@link CallVideoProvider#onSendSessionModifyResponse} to indicate
      * the video settings the user has agreed to.
      *
      * @param videoCallProfile The requested video call profile.
@@ -197,7 +196,7 @@
     /**
      * Called when a response to a session modification request is received from the remote device.
      * The remote InCall UI sends the response using
-     * {@link CallVideoProvider#sendSessionModifyResponse}.
+     * {@link CallVideoProvider#onSendSessionModifyResponse}.
      *
      * @param status Status of the session modify request.  Valid values are
      *               {@link CallVideoClient#SESSION_MODIFY_REQUEST_SUCCESS},
@@ -219,7 +218,7 @@
      *
      * @param event The event.
      */
-    public abstract void onCallSessionEvent(int event);
+    public abstract void onHandleCallSessionEvent(int event);
 
     /**
      * Handles a change to the video dimensions from the remote caller (peer).  This could happen
@@ -228,7 +227,7 @@
      * @param width  The updated peer video width.
      * @param height The updated peer video height.
      */
-    public abstract void onUpdatedPeerDimensions(int width, int height);
+    public abstract void onUpdatePeerDimensions(int width, int height);
 
     /**
      * Handles an update to the total data used for the current session.
@@ -242,6 +241,6 @@
      *
      * @param callCameraCapabilities The changed camera capabilities.
      */
-    public abstract void onCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities);
+    public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities);
 }
 
diff --git a/telecomm/java/android/telecomm/CallVideoProvider.java b/telecomm/java/android/telecomm/CallVideoProvider.java
index 2f16112..2a04724 100644
--- a/telecomm/java/android/telecomm/CallVideoProvider.java
+++ b/telecomm/java/android/telecomm/CallVideoProvider.java
@@ -17,22 +17,26 @@
 package android.telecomm;
 
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Message;
+import android.os.RemoteException;
 import android.view.Surface;
 
+import com.android.internal.telecomm.ICallVideoClient;
 import com.android.internal.telecomm.ICallVideoProvider;
 
 public abstract class CallVideoProvider {
-    private static final int MSG_SET_CAMERA = 1;
-    private static final int MSG_SET_PREVIEW_SURFACE = 2;
-    private static final int MSG_SET_DISPLAY_SURFACE = 3;
-    private static final int MSG_SET_DEVICE_ORIENTATION = 4;
-    private static final int MSG_SET_ZOOM = 5;
-    private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 6;
-    private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 7;
-    private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 8;
-    private static final int MSG_REQUEST_CALL_DATA_USAGE = 9;
-    private static final int MSG_SET_PAUSE_IMAGE = 10;
+    private static final int MSG_SET_CALL_VIDEO_CLIENT = 1;
+    private static final int MSG_SET_CAMERA = 2;
+    private static final int MSG_SET_PREVIEW_SURFACE = 3;
+    private static final int MSG_SET_DISPLAY_SURFACE = 4;
+    private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+    private static final int MSG_SET_ZOOM = 6;
+    private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+    private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+    private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+    private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+    private static final int MSG_SET_PAUSE_IMAGE = 11;
 
     /**
      * Default handler used to consolidate binder method calls onto a single thread.
@@ -41,35 +45,45 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case MSG_SET_CALL_VIDEO_CLIENT:
+                    try {
+                        ICallVideoClient callVideoClient =
+                                ICallVideoClient.Stub.asInterface((IBinder) msg.obj);
+                        RemoteCallVideoClient remoteCallVideoClient =
+                                new RemoteCallVideoClient(callVideoClient);
+                        onSetCallVideoClient(remoteCallVideoClient);
+                    } catch (RemoteException ignored) {
+                    }
+                    break;
                 case MSG_SET_CAMERA:
-                    setCamera((String) msg.obj);
+                    onSetCamera((String) msg.obj);
                     break;
                 case MSG_SET_PREVIEW_SURFACE:
-                    setPreviewSurface((Surface) msg.obj);
+                    onSetPreviewSurface((Surface) msg.obj);
                     break;
                 case MSG_SET_DISPLAY_SURFACE:
-                    setDisplaySurface((Surface) msg.obj);
+                    onSetDisplaySurface((Surface) msg.obj);
                     break;
                 case MSG_SET_DEVICE_ORIENTATION:
-                    setDeviceOrientation(msg.arg1);
+                    onSetDeviceOrientation(msg.arg1);
                     break;
                 case MSG_SET_ZOOM:
-                    setZoom((Float) msg.obj);
+                    onSetZoom((Float) msg.obj);
                     break;
                 case MSG_SEND_SESSION_MODIFY_REQUEST:
-                    sendSessionModifyRequest((VideoCallProfile) msg.obj);
+                    onSendSessionModifyRequest((VideoCallProfile) msg.obj);
                     break;
                 case MSG_SEND_SESSION_MODIFY_RESPONSE:
-                    sendSessionModifyResponse((VideoCallProfile) msg.obj);
+                    onSendSessionModifyResponse((VideoCallProfile) msg.obj);
                     break;
                 case MSG_REQUEST_CAMERA_CAPABILITIES:
-                    requestCameraCapabilities();
+                    onRequestCameraCapabilities();
                     break;
                 case MSG_REQUEST_CALL_DATA_USAGE:
-                    requestCallDataUsage();
+                    onRequestCallDataUsage();
                     break;
                 case MSG_SET_PAUSE_IMAGE:
-                    setPauseImage((String) msg.obj);
+                    onSetPauseImage((String) msg.obj);
                     break;
                 default:
                     break;
@@ -81,6 +95,11 @@
      * Default ICallVideoProvider implementation.
      */
     private final class CallVideoProviderBinder extends ICallVideoProvider.Stub {
+        public void setCallVideoClient(IBinder callVideoClientBinder) {
+            mMessageHandler.obtainMessage(
+                    MSG_SET_CALL_VIDEO_CLIENT, callVideoClientBinder).sendToTarget();
+        }
+
         public void setCamera(String cameraId) {
             mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
         }
@@ -102,13 +121,13 @@
         }
 
         public void sendSessionModifyRequest(VideoCallProfile requestProfile) {
-            mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST,
-                    requestProfile).sendToTarget();
+            mMessageHandler.obtainMessage(
+                    MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
         }
 
         public void sendSessionModifyResponse(VideoCallProfile responseProfile) {
-            mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_RESPONSE,
-                    responseProfile).sendToTarget();
+            mMessageHandler.obtainMessage(
+                    MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
         }
 
         public void requestCameraCapabilities() {
@@ -140,11 +159,19 @@
     }
 
     /**
+     * Sets a remote interface for invoking callback methods in the InCallUI after performing
+     * telephony actions.
+     *
+     * @param callVideoClient The call video client.
+     */
+    public abstract void onSetCallVideoClient(RemoteCallVideoClient callVideoClient);
+
+    /**
      * Sets the camera to be used for video recording in a video call.
      *
      * @param cameraId The id of the camera.
      */
-    public abstract void setCamera(String cameraId);
+    public abstract void onSetCamera(String cameraId);
 
     /**
      * Sets the surface to be used for displaying a preview of what the user's camera is
@@ -153,14 +180,14 @@
      *
      * @param surface The surface.
      */
-    public abstract void setPreviewSurface(Surface surface);
+    public abstract void onSetPreviewSurface(Surface surface);
 
     /**
      * Sets the surface to be used for displaying the video received from the remote device.
      *
      * @param surface The surface.
      */
-    public abstract void setDisplaySurface(Surface surface);
+    public abstract void onSetDisplaySurface(Surface surface);
 
     /**
      * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of the
@@ -168,14 +195,14 @@
      *
      * @param rotation The device orientation, in degrees.
      */
-    public abstract void setDeviceOrientation(int rotation);
+    public abstract void onSetDeviceOrientation(int rotation);
 
     /**
      * Sets camera zoom ratio.
      *
      * @param value The camera zoom ratio.
      */
-    public abstract void setZoom(float value);
+    public abstract void onSetZoom(float value);
 
     /**
      * Issues a request to modify the properties of the current session.  The request is sent to
@@ -186,7 +213,7 @@
      *
      * @param requestProfile The requested call video properties.
      */
-    public abstract void sendSessionModifyRequest(VideoCallProfile requestProfile);
+    public abstract void onSendSessionModifyRequest(VideoCallProfile requestProfile);
 
     /**
      * Provides a response to a request to change the current call session video
@@ -198,21 +225,21 @@
      *
      * @param responseProfile The response call video properties.
      */
-    public abstract void sendSessionModifyResponse(VideoCallProfile responseProfile);
+    public abstract void onSendSessionModifyResponse(VideoCallProfile responseProfile);
 
     /**
      * Issues a request to the video provider to retrieve the camera capabilities.
      * Camera capabilities are reported back to the caller via
-     * {@link CallVideoClient#onCameraCapabilitiesChange(CallCameraCapabilities)}.
+     * {@link CallVideoClient#onHandleCameraCapabilitiesChange(CallCameraCapabilities)}.
      */
-    public abstract void requestCameraCapabilities();
+    public abstract void onRequestCameraCapabilities();
 
     /**
      * Issues a request to the video telephony framework to retrieve the cumulative data usage for
      * the current call.  Data usage is reported back to the caller via
      * {@link CallVideoClient#onUpdateCallDataUsage}.
      */
-    public abstract void requestCallDataUsage();
+    public abstract void onRequestCallDataUsage();
 
     /**
      * Provides the video telephony framework with the URI of an image to be displayed to remote
@@ -220,5 +247,5 @@
      *
      * @param uri URI of image to display.
      */
-    public abstract void setPauseImage(String uri);
+    public abstract void onSetPauseImage(String uri);
 }
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index f80e4a9..73aac2c 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -34,6 +34,7 @@
     /** @hide */
     public interface Listener {
         void onStateChanged(Connection c, int state);
+        void onFeaturesChanged(Connection c, int features);
         void onAudioStateChanged(Connection c, CallAudioState state);
         void onHandleChanged(Connection c, Uri newHandle);
         void onSignalChanged(Connection c, Bundle details);
@@ -51,6 +52,11 @@
         @Override
         public void onStateChanged(Connection c, int state) {}
 
+        /** {@inheritDoc} */
+        @Override
+        public void onFeaturesChanged(Connection c, int features) {}
+
+        /** {@inheritDoc} */
          @Override
         public void onAudioStateChanged(Connection c, CallAudioState state) {}
 
@@ -97,6 +103,7 @@
     private final List<Connection> mChildConnections = new ArrayList<>();
 
     private int mState = State.NEW;
+    private int mFeatures = CallFeatures.NONE;
     private CallAudioState mCallAudioState;
     private Uri mHandle;
     private boolean mRequestingRingback = false;
@@ -131,6 +138,12 @@
     }
 
     /**
+     * @return The features of the call.  These are items for which the InCall UI may wish to
+     *         display a visual indicator.
+     */
+    public final int getFeatures() { return mFeatures; }
+
+    /**
      * @return The audio state of the call, describing how its audio is currently
      *         being routed by the system. This is {@code null} if this Connection
      *         does not directly know about its audio state.
@@ -284,6 +297,22 @@
     }
 
     /**
+     * Set the features applicable to the connection.  These are items for which the InCall UI may
+     * wish to display a visual indicator.
+     * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+     * bit-mask.
+     *
+     * @param features The features active.
+     */
+    public final void setFeatures(int features) {
+        Log.d(this, "setFeatures %d", features);
+        this.mFeatures = features;
+        for (Listener l : mListeners) {
+            l.onFeaturesChanged(this, mFeatures);
+        }
+    }
+
+    /**
      * Inform this Connection that the state of its audio output has been changed externally.
      *
      * @param state The new audio state.
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index e68efea..530e977 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -108,6 +108,14 @@
             }
         }
 
+        /** {@inheritDoc} */
+        @Override
+        public void onFeaturesChanged(Connection c, int features) {
+            String id = mIdByConnection.get(c);
+            Log.d(this, "Adapter set features %d", features);
+            getAdapter().setFeatures(id, features);
+        }
+
         @Override
         public void onDisconnected(Connection c, int cause, String message) {
             String id = mIdByConnection.get(c);
@@ -561,4 +569,18 @@
         Log.w(this, "%s - Cannot find Connection %s", action, callId);
         return NULL_CONNECTION;
     }
+
+    /**
+     * Handles changes to the features of a connection.
+     * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+     * bit-mask.
+     *
+     * @param callId The call to set the features for.
+     * @param features The new features of the call.
+     */
+    @Override
+    public final void onFeaturesChanged(String callId, int features) {
+        Log.d(this, "onFeaturesChanged %s %d", callId, features);
+        findConnectionForAction(callId, "onFeaturesChanged").setFeatures(features);
+    }
 }
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index cf31cec5..44dd567 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -48,6 +48,7 @@
     private RemoteCallVideoProvider mRemoteCallVideoProvider;
     private final String mParentCallId;
     private final List<String> mChildCallIds;
+    private final int mFeatures;
 
     /** @hide */
     public InCallCall(
@@ -65,7 +66,8 @@
             CallServiceDescriptor handoffDescriptor,
             ICallVideoProvider callVideoProvider,
             String parentCallId,
-            List<String> childCallIds) {
+            List<String> childCallIds,
+            int features) {
         mId = id;
         mState = state;
         mDisconnectCauseCode = disconnectCauseCode;
@@ -81,6 +83,7 @@
         mCallVideoProvider = callVideoProvider;
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
+        mFeatures = features;
     }
 
     /** The unique ID of the call. */
@@ -187,6 +190,15 @@
         return mChildCallIds;
     }
 
+    /**
+     * The features of this call (e.g. VoLTE, VoWIFI).
+     *
+     * @return Features.
+     */
+    public int getFeatures() {
+        return mFeatures;
+    }
+
     /** Responsible for creating InCallCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<InCallCall> CREATOR =
             new Parcelable.Creator<InCallCall> () {
@@ -211,10 +223,11 @@
             String parentCallId = source.readString();
             List<String> childCallIds = new ArrayList<>();
             source.readList(childCallIds, classLoader);
+            int features = source.readInt();
             return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg,
                     cannedSmsResponses, capabilities, connectTimeMillis, handle, gatewayInfo,
                     subscription, descriptor, handoffDescriptor, callVideoProvider, parentCallId,
-                    childCallIds);
+                    childCallIds, features);
         }
 
         @Override
@@ -248,6 +261,7 @@
                 mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null);
         destination.writeString(mParentCallId);
         destination.writeList(mChildCallIds);
+        destination.writeInt(mFeatures);
     }
 
     @Override
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoClient.java b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
new file mode 100644
index 0000000..f0a3afc
--- /dev/null
+++ b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
@@ -0,0 +1,65 @@
+/*
+ * 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.telecomm;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telecomm.CallCameraCapabilities;
+import android.telecomm.VideoCallProfile;
+
+import com.android.internal.telecomm.ICallVideoClient;
+
+public class RemoteCallVideoClient implements IBinder.DeathRecipient {
+    private final ICallVideoClient mCallVideoClient;
+
+    RemoteCallVideoClient(ICallVideoClient callVideoProvider) throws RemoteException {
+        mCallVideoClient = callVideoProvider;
+        mCallVideoClient.asBinder().linkToDeath(this, 0);
+    }
+
+    @Override
+    public void binderDied() {
+        mCallVideoClient.asBinder().unlinkToDeath(this, 0);
+    }
+
+    public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile)
+            throws RemoteException {
+        mCallVideoClient.receiveSessionModifyRequest(videoCallProfile);
+    }
+
+    public void receiveSessionModifyResponse(int status, VideoCallProfile requestedProfile,
+            VideoCallProfile responseProfile) throws RemoteException {
+        mCallVideoClient.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+    }
+
+    public void handleCallSessionEvent(int event) throws RemoteException {
+        mCallVideoClient.handleCallSessionEvent(event);
+    }
+
+    public void updatePeerDimensions(int width, int height) throws RemoteException {
+        mCallVideoClient.updatePeerDimensions(width, height);
+    }
+
+    public void updateCallDataUsage(int dataUsage) throws RemoteException {
+        mCallVideoClient.updateCallDataUsage(dataUsage);
+    }
+
+    public void handleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities)
+            throws RemoteException {
+        mCallVideoClient.handleCameraCapabilitiesChange(callCameraCapabilities);
+    }
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
index 872085a..856d321 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
@@ -18,10 +18,11 @@
 
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.view.Surface;
 
+import com.android.internal.telecomm.ICallVideoClient;
 import com.android.internal.telecomm.ICallVideoProvider;
 
-
 public class RemoteCallVideoProvider implements IBinder.DeathRecipient {
     private final ICallVideoProvider mCallVideoProvider;
 
@@ -35,12 +36,47 @@
         mCallVideoProvider.asBinder().unlinkToDeath(this, 0);
     }
 
-    /**
-     * Sets the camera to be used for video recording in a video call, using the binder.
-     *
-     * @param cameraId The id of the camera.
-     */
+    public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException {
+        mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder());
+    }
+
     public void setCamera(String cameraId) throws RemoteException {
         mCallVideoProvider.setCamera(cameraId);
     }
-}
+
+    public void setPreviewSurface(Surface surface) throws RemoteException {
+        mCallVideoProvider.setPreviewSurface(surface);
+    }
+
+    public void setDisplaySurface(Surface surface) throws RemoteException {
+        mCallVideoProvider.setDisplaySurface(surface);
+    }
+
+    public void setDeviceOrientation(int rotation) throws RemoteException {
+        mCallVideoProvider.setDeviceOrientation(rotation);
+    }
+
+    public void setZoom(float value) throws RemoteException {
+        mCallVideoProvider.setZoom(value);
+    }
+
+    public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException {
+        mCallVideoProvider.sendSessionModifyRequest(requestProfile);
+    }
+
+    public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException {
+        mCallVideoProvider.sendSessionModifyResponse(responseProfile);
+    }
+
+    public void requestCameraCapabilities() throws RemoteException {
+        mCallVideoProvider.requestCameraCapabilities();
+    }
+
+    public void requestCallDataUsage() throws RemoteException {
+        mCallVideoProvider.requestCallDataUsage();
+    }
+
+    public void setPauseImage(String uri) throws RemoteException {
+        mCallVideoProvider.setPauseImage(uri);
+    }
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index a159dee..f200bc0 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -43,7 +43,7 @@
     private final String mConnectionId;
     private final Set<Listener> mListeners = new HashSet<>();
 
-    private int mState;
+    private int mState = Connection.State.NEW;
     private int mDisconnectCause = DisconnectCause.NOT_VALID;
     private String mDisconnectMessage;
     private boolean mRequestingRingback;
@@ -67,6 +67,10 @@
         mListeners.remove(listener);
     }
 
+    public int getState() {
+        return mState;
+    }
+
     public int getDisconnectCause() {
         return mDisconnectCause;
     }
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 02a100e..f6fc69f 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -193,6 +193,12 @@
             } catch (RemoteException e) {
             }
         }
+
+        /** ${inheritDoc} */
+        @Override
+        public void setFeatures(String connectionId, int features) {
+            // not supported for remote connections.
+        }
     };
 
     RemoteConnectionService(ComponentName componentName, ICallService callService)
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index a0abc28..6bb75be 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -1,17 +1,15 @@
 /*
  * 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
+ * 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
+ * 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.
+ * 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;
@@ -62,14 +60,111 @@
     @SystemApi
     public ComponentName getDefaultPhoneApp() {
         try {
-            return getTelecommService().getDefaultPhoneApp();
+            if (isServiceConnected()) {
+                return getTelecommService().getDefaultPhoneApp();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
         }
         return null;
     }
 
+    /**
+     * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
+     * states).
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isInAPhoneCall() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().isInAPhoneCall();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException attempting to get default phone app.", e);
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether there currently exists is a ringing incoming-call.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isRinging() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().isRinging();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
+        }
+        return false;
+    }
+
+    /**
+     * Ends an ongoing call. TODO(santoscordon): L-release - need to convert all invocations of
+     * ITelephony#endCall to use this method (clockwork & gearhead).
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean endCall() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().endCall();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#endCall", e);
+        }
+        return false;
+    }
+
+    /**
+     * If there is a ringing incoming call, this method accepts the call on behalf of the user.
+     * TODO(santoscordon): L-release - need to convert all invocation of
+     * ITelephony#answerRingingCall to use this method (clockwork & gearhead).
+     *
+     * @hide
+     */
+    @SystemApi
+    public void acceptRingingCall() {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().acceptRingingCall();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#acceptRingingCall", e);
+        }
+    }
+
+    /**
+     * Silences the ringer if a ringing call exists.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void silenceRinger() {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().silenceRinger();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
+        }
+    }
+
     private ITelecommService getTelecommService() {
         return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
     }
+
+    private boolean isServiceConnected() {
+        boolean isConnected = getTelecommService() != null;
+        if (!isConnected) {
+            Log.w(TAG, "Telecomm Service not found.");
+        }
+        return isConnected;
+    }
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index b81ef37..e78762d 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -65,4 +65,6 @@
     void queryRemoteConnectionServices(RemoteServiceCallback callback);
 
     void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider);
+
+    void setFeatures(String callId, int features);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl b/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl
index 0a854a1..2689561 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl
@@ -29,16 +29,16 @@
   */
  oneway interface ICallVideoClient {
 
-     void onReceiveSessionModifyRequest(in VideoCallProfile videoCallProfile);
+     void receiveSessionModifyRequest(in VideoCallProfile videoCallProfile);
 
-     void onReceiveSessionModifyResponse(int status, in VideoCallProfile requestedProfile,
+     void receiveSessionModifyResponse(int status, in VideoCallProfile requestedProfile,
             in VideoCallProfile responseProfile);
 
-     void onCallSessionEvent(int event);
+     void handleCallSessionEvent(int event);
 
-     void onUpdatedPeerDimensions(int width, int height);
+     void updatePeerDimensions(int width, int height);
 
-     void onUpdateCallDataUsage(int dataUsage);
+     void updateCallDataUsage(int dataUsage);
 
-     void onCameraCapabilitiesChange(in CallCameraCapabilities callCameraCapabilities);
+     void handleCameraCapabilitiesChange(in CallCameraCapabilities callCameraCapabilities);
  }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl b/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
index f6587b7..860a431 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
@@ -19,12 +19,16 @@
 import android.view.Surface;
 import android.telecomm.VideoCallProfile;
 
+import com.android.internal.telecomm.ICallVideoClient;
+
 /**
  * Internal remote interface for a call video provider.
  * @see android.telecomm.CallVideoProvider
  * @hide
  */
 oneway interface ICallVideoProvider {
+    void setCallVideoClient(IBinder callVideoClient);
+
     void setCamera(String cameraId);
 
     void setPreviewSurface(in Surface surface);
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 2ae5768..65389df 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -25,16 +25,6 @@
  * {@hide}
  */
 interface ITelecommService {
-
-    /**
-     * Silence the ringer if an incoming call is currently ringing.
-     * (If vibrating, stop the vibrator also.)
-     *
-     * It's safe to call this if the ringer has already been silenced, or
-     * even if there's no incoming call.  (If so, this method will do nothing.)
-     */
-    void silenceRinger();
-
     /**
      * Brings the in-call screen to the foreground if there is an active call.
      *
@@ -61,4 +51,33 @@
      * Returns the component name of the default phone application.
      */
     ComponentName getDefaultPhoneApp();
+
+    //
+    // Internal system apis relating to call management.
+    //
+
+    /**
+     * @see TelecommManager#silenceRinger
+     */
+    void silenceRinger();
+
+    /**
+     * @see TelecommManager#isInAPhoneCall
+     */
+    boolean isInAPhoneCall();
+
+    /**
+     * @see TelecomManager#isRinging
+     */
+    boolean isRinging();
+
+    /**
+     * @see TelecommManager#endCall
+     */
+    boolean endCall();
+
+    /**
+     * @see TelecommManager#acceptRingingCall
+     */
+    void acceptRingingCall();
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 124a8ec..0e5b0e6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2793,6 +2793,25 @@
     }
 
     /**
+     * Set the CDMA subscription source.
+     * Used for device supporting both NV and RUIM for CDMA.
+     *
+     * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
+     * @return true on success; false on any failure.
+     * @hide
+     */
+    public boolean setCdmaSubscription(int subscriptionType) {
+        try {
+            return getITelephony().setCdmaSubscription(subscriptionType);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setCdmaSubscription RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setCdmaSubscription NPE", ex);
+        }
+        return false;
+    }
+
+    /**
      * Expose the rest of ITelephony to @SystemApi
      */
 
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index 32929ed..f9375e4 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -47,6 +47,26 @@
     int queryCallWaiting();
 
     /**
+     * Retrieves the default CLIR setting.
+     */
+    int queryCLIR();
+
+    /**
+     * Retrieves the CLIP call setting.
+     */
+    int queryCLIP();
+
+    /**
+     * Retrieves the COLR call setting.
+     */
+    int queryCOLR();
+
+    /**
+     * Retrieves the COLP call setting.
+     */
+    int queryCOLP();
+
+    /**
      * Updates or retrieves the supplementary service configuration.
      */
     int transact(in Bundle ssInfo);
@@ -67,6 +87,26 @@
     int updateCallWaiting(boolean enable);
 
     /**
+     * Updates the configuration of the CLIR supplementary service.
+     */
+    int updateCLIR(int clirMode);
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     */
+    int updateCLIP(boolean enable);
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     */
+    int updateCOLR(int presentation);
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     */
+    int updateCOLP(boolean enable);
+
+    /**
      * Sets the listener.
      */
     void setListener(in IImsUtListener listener);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index beee616..237e7f3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -606,6 +606,15 @@
     boolean setPreferredNetworkType(int networkType);
 
     /**
+     * Set the CDMA subscription source.
+     * Used for device supporting both NV and RUIM for CDMA.
+     *
+     * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
+     * @return true on success; false on any failure.
+     */
+    boolean setCdmaSubscription(int subscriptionType);
+
+    /**
      * User enable/disable Mobile Data.
      *
      * @param enable true to turn on, else false
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 59036da..a14714a 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -34,6 +34,7 @@
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -744,7 +745,7 @@
     /**
      * @hide
      */
-    public Bitmap getUserIcon(int userId) {
+    public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
index 2ea5219..1bb6d0c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
@@ -22,6 +22,8 @@
 import android.content.pm.ConfigurationInfo;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.opengl.EGL14;
+import android.opengl.EGLDisplay;
 import android.opengl.GLES20;
 import android.opengl.GLSurfaceView;
 import android.opengl.GLUtils;
@@ -29,7 +31,7 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.Log;
-
+import android.view.MotionEvent;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -54,7 +56,8 @@
             // 2.0-compatible
             // context, and set an OpenGL ES 2.0-compatible renderer.
             mGLSurfaceView.setEGLContextClientVersion(2);
-            mGLSurfaceView.setRenderer(new GLES20TriangleRenderer(this));
+            mRenderer = new GLES20TriangleRenderer(this);
+            mGLSurfaceView.setRenderer(mRenderer);
         } else {
             throw new IllegalStateException("Can't find OGL ES2.0 context");
         }
@@ -84,7 +87,17 @@
         mGLSurfaceView.onPause();
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        Log.i("motion", event.toString());
+        if (event.getActionMasked() ==  MotionEvent.ACTION_DOWN) {
+            mRenderer.toggleDepthTest();
+        }
+        return true;
+    }
+
     private GLSurfaceView mGLSurfaceView;
+    private GLES20TriangleRenderer mRenderer;
 
     /*
      * Copyright (C) 2009 The Android Open Source Project Licensed under the
@@ -99,7 +112,9 @@
      */
 
     class GLES20TriangleRenderer implements GLSurfaceView.Renderer {
-
+        private final static int REPEAT_RECTANGLES = 10;
+        private boolean mDepthTestEnabled = true;
+        private final static int FRAME_REPEAT_TIMES = 1;
         public GLES20TriangleRenderer(Context context) {
             mContext = context;
             mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
@@ -107,39 +122,60 @@
             mTriangleVertices.put(mTriangleVerticesData).position(0);
         }
 
+
+        public void toggleDepthTest() {
+            mDepthTestEnabled = !mDepthTestEnabled;
+            Log.v(TAG, "mDepthTestEnabled is " + mDepthTestEnabled);
+        }
+
         public void onDrawFrame(GL10 glUnused) {
-            // Ignore the passed-in GL10 interface, and use the GLES20
-            // class's static methods instead.
-            GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
-            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-            GLES20.glUseProgram(mProgram);
-            checkGlError("glUseProgram");
+            for (int j = 0 ; j < FRAME_REPEAT_TIMES; j ++) {
+                // Ignore the passed-in GL10 interface, and use the GLES20
+                // class's static methods instead.
+                GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+                if (mDepthTestEnabled) {
+                    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+                } else {
+                    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                }
+                GLES20.glUseProgram(mProgram);
+                if (mDepthTestEnabled) {
+                    GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+                } else {
+                    GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+                }
+                checkGlError("glUseProgram");
 
-            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
+                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
 
-            mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
-            GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
-                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
-            checkGlError("glVertexAttribPointer maPosition");
-            mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
-            GLES20.glEnableVertexAttribArray(maPositionHandle);
-            checkGlError("glEnableVertexAttribArray maPositionHandle");
-            GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
-                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
-            checkGlError("glVertexAttribPointer maTextureHandle");
-            GLES20.glEnableVertexAttribArray(maTextureHandle);
-            checkGlError("glEnableVertexAttribArray maTextureHandle");
+                mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+                GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+                        TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+                checkGlError("glVertexAttribPointer maPosition");
+                mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+                GLES20.glEnableVertexAttribArray(maPositionHandle);
+                checkGlError("glEnableVertexAttribArray maPositionHandle");
+                GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
+                        TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+                checkGlError("glVertexAttribPointer maTextureHandle");
+                GLES20.glEnableVertexAttribArray(maTextureHandle);
+                checkGlError("glEnableVertexAttribArray maTextureHandle");
 
-            long time = SystemClock.uptimeMillis() % 4000L;
-            float angle = 0.090f * ((int) time);
-            Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
-            Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
-            Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
+                for (int i = 0 ; i < REPEAT_RECTANGLES; i ++) {
+                    float step = ((float)i) / REPEAT_RECTANGLES;
+                    Matrix.setIdentityM(mMMatrix, 0);
+                    Matrix.translateM(mMMatrix, 0, 0, step, step / 2);
+                    Matrix.scaleM(mMMatrix, 0, 2.0f, 1.0f, 1.0f);
+                    Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
+                    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
 
-            GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
-            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
-            checkGlError("glDrawArrays");
+                    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
+                    GLES20.glUniform4f(muOverlayHandle, step , step, step , step);
+                    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+                    checkGlError("glDrawArrays");
+                }
+            }
         }
 
         public void onSurfaceChanged(GL10 glUnused, int width, int height) {
@@ -174,6 +210,12 @@
                 throw new RuntimeException("Could not get attrib location for uMVPMatrix");
             }
 
+            muOverlayHandle = GLES20.glGetUniformLocation(mProgram, "uOverlay");
+            checkGlError("glGetUniformLocation uOverlay");
+            if (muOverlayHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for muOverlayHandle");
+            }
+
             /*
              * Create our texture. This has to be done each time the surface is
              * created.
@@ -213,6 +255,10 @@
             bitmap.recycle();
 
             Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+            EGLDisplay display = EGL14.eglGetCurrentDisplay();
+            EGL14.eglSwapInterval(display, 0);
+
         }
 
         private int loadShader(int shaderType, String source) {
@@ -276,9 +322,10 @@
         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
         private final float[] mTriangleVerticesData = {
                                 // X, Y, Z, U, V
-                -1.0f, -0.5f, 0, -0.5f, 0.0f,
-                1.0f, -0.5f, 0, 1.5f, -0.0f,
-                0.0f, 1.11803399f, 0, 0.5f, 1.61803399f };
+                -1.0f, -1.0f, 0, 0.0f, 0.0f,
+                -1.0f, 1.0f, 0, 0.0f, 1.0f,
+                1.0f, -1.0f, 0, 1.0f, 0.0f,
+                1.0f, 1.0f, 0, 1.0f, 1.0f, };
 
         private FloatBuffer mTriangleVertices;
 
@@ -296,8 +343,9 @@
                 "precision mediump float;\n" +
                         "varying vec2 vTextureCoord;\n" +
                         "uniform sampler2D sTexture;\n" +
+                        "uniform vec4 uOverlay;\n" +
                         "void main() {\n" +
-                        "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+                        "  gl_FragColor = texture2D(sTexture, vTextureCoord) * uOverlay;\n" +
                         "}\n";
 
         private float[] mMVPMatrix = new float[16];
@@ -310,6 +358,7 @@
         private int muMVPMatrixHandle;
         private int maPositionHandle;
         private int maTextureHandle;
+        private int muOverlayHandle;
 
         private Context mContext;
         private static final String TAG = "GLES20TriangleRenderer";
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 890214f..613660c13 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -99,7 +99,7 @@
         </activity>
         <activity
             android:name="VectorCheckbox"
-            android:label="On a Checkbox" >
+            android:label="Basic static vector drawables" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
@@ -115,6 +115,15 @@
                 <category android:name="com.android.test.dynamic.TEST" />
             </intent-filter>
         </activity>
+        <activity
+            android:name="ScaleDrawableTests"
+            android:label="Scale Type Test" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="com.android.test.dynamic.TEST" />
+            </intent-filter>
+        </activity>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
new file mode 100644
index 0000000..07ca2eb
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
@@ -0,0 +1,97 @@
+/*
+ * 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.test.dynamic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.GridLayout;
+import android.widget.ScrollView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ScaleDrawableTests extends Activity {
+    private static final String LOGCAT = "VectorDrawable1";
+
+    private String[] scaleTypes = {
+            "MATRIX      (0)",
+            "FIT_XY      (1)",
+            "FIT_START   (2)",
+            "FIT_CENTER  (3)",
+            "FIT_END     (4)",
+            "CENTER      (5)",
+            "CENTER_CROP (6)",
+            "CENTER_INSIDE (7)"
+    };
+
+    protected int[] icon = {
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+    };
+
+    protected int[] vector_icons = {
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+            R.drawable.vector_drawable16,
+    };
+
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ScrollView scrollView = new ScrollView(this);
+        GridLayout container = new GridLayout(this);
+        scrollView.addView(container);
+        container.setColumnCount(3);
+        container.setBackgroundColor(0xFF888888);
+
+        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        params.width = 400;
+        params.height = 300;
+
+        for (int i = 0; i < icon.length; i++) {
+            TextView t = new TextView(this);
+            t.setText(scaleTypes[i]);
+            container.addView(t);
+
+            ImageView png_view = new ImageView(this);
+            png_view.setLayoutParams(params);
+            png_view.setScaleType(ImageView.ScaleType.values()[i]);
+            png_view.setImageResource(icon[i]);
+            container.addView(png_view);
+
+            ImageView view = new ImageView(this);
+            view.setLayoutParams(params);
+            view.setScaleType(ImageView.ScaleType.values()[i]);
+            view.setImageResource(vector_icons[i]);
+            container.addView(view);
+        }
+
+        setContentView(scrollView);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 33813d1..88ebd1f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -28,6 +28,8 @@
 
 import com.ibm.icu.lang.UScript;
 import com.ibm.icu.lang.UScriptRun;
+import com.ibm.icu.text.Bidi;
+import com.ibm.icu.text.BidiRun;
 
 import android.graphics.Paint_Delegate.FontInfo;
 
@@ -38,7 +40,7 @@
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
 
-    /*package*/ static class ScriptRun {
+    private static class ScriptRun {
         int start;
         int limit;
         boolean isRtl;
@@ -66,7 +68,7 @@
      * @param paint The Paint to use to get the fonts. Should not be null.
      * @param text Unidirectional text. Should not be null.
      */
-    /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+    public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
         assert (paint != null);
         mGraphics = graphics;
         mPaint = paint;
@@ -75,13 +77,45 @@
         for (FontInfo fontInfo : paint.getFonts()) {
             mFonts.add(fontInfo.mFont);
         }
+        mBounds = new RectF();
+    }
+
+    /**
+     *
+     * @param x The x-coordinate of the left edge of where the text should be drawn on the given
+     *            graphics.
+     * @param y The y-coordinate at which to draw the text on the given mGraphics.
+     *
+     */
+    public BidiRenderer setRenderLocation(float x, float y) {
+        mBounds = new RectF(x, y, x, y);
+        mBaseline = y;
+        return this;
+    }
+
+    /**
+     * Perform Bidi Analysis on the text and then render it.
+     * <p/>
+     * To skip the analysis and render unidirectional text, see {@link
+     * #renderText(int, int, boolean, float[], int, boolean)}
+     */
+    public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
+            int advancesIndex, boolean draw) {
+        Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
+        for (int i = 0; i < bidi.countRuns(); i++) {
+            BidiRun visualRun = bidi.getVisualRun(i);
+            boolean isRtl = visualRun.getDirection() == Bidi.RTL;
+            renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
+                    advancesIndex, draw);
+        }
+        return mBounds;
     }
 
     /**
      * Render unidirectional text.
-     *
+     * <p/>
      * This method can also be used to measure the width of the text without actually drawing it.
-     *
+     * <p/>
      * @param start index of the first character
      * @param limit index of the first character that should not be rendered.
      * @param isRtl is the text right-to-left
@@ -90,17 +124,12 @@
      * @param advancesIndex index into advances from where the advances need to be filled.
      * @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics
      *            at the given co-ordinates
-     * @param x The x-coordinate of the left edge of where the text should be drawn on the given
-     *            graphics.
-     * @param y The y-coordinate at which to draw the text on the given mGraphics.
      * @return A rectangle specifying the bounds of the text drawn.
      */
-    /* package */ RectF renderText(int start, int limit, boolean isRtl, float[] advances,
-            int advancesIndex, boolean draw, float x, float y) {
+    public RectF renderText(int start, int limit, boolean isRtl, float[] advances,
+            int advancesIndex, boolean draw) {
         // We break the text into scripts and then select font based on it and then render each of
         // the script runs.
-        mBounds = new RectF(x, y, x, y);
-        mBaseline = y;
         for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
             int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
             flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
@@ -244,4 +273,22 @@
         }
         run.font = fonts.get(0);
     }
+
+    private static int getIcuFlags(int bidiFlag) {
+        switch (bidiFlag) {
+            case Paint.BIDI_LTR:
+            case Paint.BIDI_FORCE_LTR:
+                return Bidi.DIRECTION_LEFT_TO_RIGHT;
+            case Paint.BIDI_RTL:
+            case Paint.BIDI_FORCE_RTL:
+                return Bidi.DIRECTION_RIGHT_TO_LEFT;
+            case Paint.BIDI_DEFAULT_LTR:
+                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+            case Paint.BIDI_DEFAULT_RTL:
+                return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
+            default:
+                assert false;
+                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+        }
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 7016136..bd88ae2 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -315,7 +315,7 @@
 
     @LayoutlibDelegate
     /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
-            int config, int allocSize) {
+            int config, int allocSize, boolean isPremultiplied) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                 "Bitmap.reconfigure() is not supported", null /*data*/);
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 2ee06fc..7c8ef70 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -55,16 +55,19 @@
     private static final DelegateManager<Canvas_Delegate> sManager =
             new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
 
+
     // ---- delegate helper data ----
 
     private final static boolean[] sBoolOut = new boolean[1];
 
+
     // ---- delegate data ----
     private Bitmap_Delegate mBitmap;
     private GcSnapshot mSnapshot;
 
     private DrawFilter_Delegate mDrawFilter = null;
 
+
     // ---- Public Helper methods ----
 
     /**
@@ -100,206 +103,6 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static boolean isOpaque(Canvas thisCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return false;
-        }
-
-        return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int getWidth(Canvas thisCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.mBitmap.getImage().getWidth();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int getHeight(Canvas thisCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.mBitmap.getImage().getHeight();
-    }
-
-    @LayoutlibDelegate
-   /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.getSnapshot().translate(dx, dy);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
-    }
-
-    @LayoutlibDelegate
-   /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.getSnapshot().scale(sx, sy);
-    }
-
-    @LayoutlibDelegate
-   /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the current top graphics2D object.
-        GcSnapshot g = canvasDelegate.getSnapshot();
-
-        // get its current matrix
-        AffineTransform currentTx = g.getTransform();
-        // get the AffineTransform for the given skew.
-        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
-        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
-
-        // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(currentTx);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
-        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
-        return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
-                (float) rect.right, (float) rect.bottom);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
-            float bottom) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return false;
-        }
-
-        return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
-            int bottom) {
-
-        return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int save(Canvas thisCanvas) {
-        return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.save(saveFlags);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void restore(Canvas thisCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.restore();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int getSaveCount(Canvas thisCanvas) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.getSnapshot().size();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.restoreTo(saveCount);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
-            Paint paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPoint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPoint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void drawLines(Canvas thisCanvas,
-            final float[] pts, final int offset, final int count,
-            Paint paint) {
-        draw(thisCanvas.getNativeCanvasWrapper(), paint.mNativePaint, false /*compositeOnly*/,
-                false /*forceSrcMode*/, new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        for (int i = 0 ; i < count ; i += 4) {
-                            graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
-                                    (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
-                        }
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
     /*package*/ static void freeCaches() {
         // nothing to be done here.
     }
@@ -328,36 +131,73 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void copyNativeCanvasState(long srcCanvas, long dstCanvas) {
+    /*package*/ static long initCanvas(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate srcCanvasDelegate = sManager.getDelegate(srcCanvas);
-        if (srcCanvasDelegate == null) {
-            return;
+        Canvas_Delegate nativeCanvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (nativeCanvasDelegate == null) {
+            return 0;
         }
 
-        // get the delegate from the native int.
-        Canvas_Delegate dstCanvasDelegate = sManager.getDelegate(dstCanvas);
-        if (dstCanvasDelegate == null) {
-            return;
-        }
+        Canvas_Delegate newDelegate = new Canvas_Delegate();
+
         // TODO: actually copy the canvas state.
+        return sManager.addNewDelegate(newDelegate);
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_saveLayer(long nativeCanvas, RectF bounds,
-                                               long paint, int layerFlags) {
+    /*package*/
+    static void native_setBitmap(long canvas, long bitmap, boolean copyState) {
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (canvasDelegate == null || bitmapDelegate==null) {
+            return;
+        }
+        canvasDelegate.mBitmap = bitmapDelegate;
+        canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_isOpaque(long nativeCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return false;
+        }
+
+        return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getWidth(long nativeCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
 
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
-        if (paintDelegate == null) {
+        return canvasDelegate.mBitmap.getImage().getWidth();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getHeight(long nativeCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
             return 0;
         }
 
-        return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
+        return canvasDelegate.mBitmap.getImage().getHeight();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_save(long nativeCanvas, int saveFlags) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.save(saveFlags);
     }
 
     @LayoutlibDelegate
@@ -380,19 +220,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_saveLayerAlpha(long nativeCanvas,
-                                                    RectF bounds, int alpha,
-                                                    int layerFlags) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return 0;
-        }
-
-        return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
-    }
-
-    @LayoutlibDelegate
     /*package*/ static int native_saveLayerAlpha(long nativeCanvas, float l,
                                                     float t, float r, float b,
                                                     int alpha, int layerFlags) {
@@ -405,6 +232,95 @@
         return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void native_restore(long nativeCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.restore();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.restoreTo(saveCount);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int native_getSaveCount(long nativeCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return 0;
+        }
+
+        return canvasDelegate.getSnapshot().size();
+    }
+
+    @LayoutlibDelegate
+   /*package*/ static void native_translate(long nativeCanvas, float dx, float dy) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.getSnapshot().translate(dx, dy);
+    }
+
+    @LayoutlibDelegate
+       /*package*/ static void native_scale(long nativeCanvas, float sx, float sy) {
+            // get the delegate from the native int.
+            Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+            if (canvasDelegate == null) {
+                return;
+            }
+
+            canvasDelegate.getSnapshot().scale(sx, sy);
+        }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_rotate(long nativeCanvas, float degrees) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
+    }
+
+    @LayoutlibDelegate
+   /*package*/ static void native_skew(long nativeCanvas, float kx, float ky) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the current top graphics2D object.
+        GcSnapshot g = canvasDelegate.getSnapshot();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform for the given skew.
+        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
+        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
+    }
 
     @LayoutlibDelegate
     /*package*/ static void native_concat(long nCanvas, long nMatrix) {
@@ -469,7 +385,6 @@
                                                   float left, float top,
                                                   float right, float bottom,
                                                   int regionOp) {
-
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
         if (canvasDelegate == null) {
@@ -568,15 +483,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean native_quickReject(long nativeCanvas,
-                                                     RectF rect) {
-        // FIXME properly implement quickReject
-        return false;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static boolean native_quickReject(long nativeCanvas,
-                                                     long path) {
+    /*package*/ static boolean native_quickReject(long nativeCanvas, long path) {
         // FIXME properly implement quickReject
         return false;
     }
@@ -645,10 +552,25 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void native_drawPoint(long nativeCanvas, float x, float y,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void native_drawLine(long nativeCanvas,
             final float startX, final float startY, final float stopX, final float stopY,
             long paint) {
-
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
                     @Override
@@ -659,8 +581,19 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_drawRect(long nativeCanvas, RectF rect, long paint) {
-        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
+    /*package*/ static void native_drawLines(long nativeCanvas,
+            final float[] pts, final int offset, final int count,
+            long nativePaint) {
+        draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
+                false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+                    @Override
+                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
+                        for (int i = 0; i < count; i += 4) {
+                            graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
+                                    (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
+                        }
+                    }
+                });
     }
 
     @LayoutlibDelegate
@@ -690,8 +623,9 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_drawOval(long nativeCanvas, final RectF oval, long paint) {
-        if (oval.right > oval.left && oval.bottom > oval.top) {
+    /*package*/ static void native_drawOval(long nativeCanvas, final float left,
+            final float top, final float right, final float bottom, long paint) {
+        if (right > left && bottom > top) {
             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                     new GcSnapshot.Drawable() {
                         @Override
@@ -701,14 +635,14 @@
                             // draw
                             if (style == Paint.Style.FILL.nativeInt ||
                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.fillOval((int)oval.left, (int)oval.top,
-                                        (int)oval.width(), (int)oval.height());
+                                graphics.fillOval((int)left, (int)top,
+                                        (int)(right - left), (int)(bottom - top));
                             }
 
                             if (style == Paint.Style.STROKE.nativeInt ||
                                     style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.drawOval((int)oval.left, (int)oval.top,
-                                        (int)oval.width(), (int)oval.height());
+                                graphics.drawOval((int)left, (int)top,
+                                        (int)(right - left), (int)(bottom - top));
                             }
                         }
             });
@@ -719,15 +653,16 @@
     /*package*/ static void native_drawCircle(long nativeCanvas,
             float cx, float cy, float radius, long paint) {
         native_drawOval(nativeCanvas,
-                new RectF(cx - radius, cy - radius, cx + radius, cy + radius),
+                cx - radius, cy - radius, cx + radius, cy + radius,
                 paint);
     }
 
     @LayoutlibDelegate
     /*package*/ static void native_drawArc(long nativeCanvas,
-            final RectF oval, final float startAngle, final float sweep,
+            final float left, final float top, final float right, final float bottom,
+            final float startAngle, final float sweep,
             final boolean useCenter, long paint) {
-        if (oval.right > oval.left && oval.bottom > oval.top) {
+        if (right > left && bottom > top) {
             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                     new GcSnapshot.Drawable() {
                         @Override
@@ -735,7 +670,7 @@
                             int style = paintDelegate.getStyle();
 
                             Arc2D.Float arc = new Arc2D.Float(
-                                    oval.left, oval.top, oval.width(), oval.height(),
+                                    left, top, right - left, bottom - top,
                                     -startAngle, -sweep,
                                     useCenter ? Arc2D.PIE : Arc2D.OPEN);
 
@@ -758,7 +693,6 @@
     /*package*/ static void native_drawRoundRect(long nativeCanvas,
             final float left, final float top, final float right, final float bottom,
             final float rx, final float ry, long paint) {
-
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
                     @Override
@@ -890,7 +824,6 @@
                                                  final float y, int width, int height,
                                                  boolean hasAlpha,
                                                  long nativePaintOrZero) {
-
         // create a temp BufferedImage containing the content.
         final BufferedImage image = new BufferedImage(width, height,
                 hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
@@ -973,41 +906,10 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_drawText(long nativeCanvas,
-            final char[] text, final int index, final int count,
-            final float startX, final float startY, final int flags, long paint,
-            final long typeface) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
-                // Any change to this method should be reflected in Paint.measureText
-
-                // assert that the typeface passed is actually the one stored in paint.
-                assert (typeface == paintDelegate.mNativeTypeface);
-
-
-                // Paint.TextAlign indicates how the text is positioned relative to X.
-                // LEFT is the default and there's nothing to do.
-                float x = startX;
-                int limit = index + count;
-                boolean isRtl = flags == Canvas.DIRECTION_RTL;
-                if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
-                    RectF bounds = paintDelegate.measureText(text, index, count, isRtl);
-                    float m = bounds.right - bounds.left;
-                    if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
-                        x -= m / 2;
-                    } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
-                        x -= m;
-                    }
-                }
-
-                new BidiRenderer(graphics, paintDelegate, text).renderText(
-                        index, limit, isRtl, null, 0, true, x, startY);
-            }
-        });
+    /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count,
+            float startX, float startY, int flags, long paint, long typeface) {
+        drawText(nativeCanvas, text, index, count, startX, startY, flags == Canvas.DIRECTION_RTL,
+                paint, typeface);
     }
 
     @LayoutlibDelegate
@@ -1024,38 +926,19 @@
     @LayoutlibDelegate
     /*package*/ static void native_drawTextRun(long nativeCanvas, String text,
             int start, int end, int contextStart, int contextEnd,
-            float x, float y, int flags, long paint, long typeface) {
+            float x, float y, boolean isRtl, long paint, long typeface) {
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
+        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
     }
 
     @LayoutlibDelegate
     /*package*/ static void native_drawTextRun(long nativeCanvas, char[] text,
             int start, int count, int contextStart, int contextCount,
-            float x, float y, int flags, long paint, long typeface) {
-        native_drawText(nativeCanvas, text, start, count, x, y, flags, paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_drawPosText(long nativeCanvas,
-                                                  char[] text, int index,
-                                                  int count, float[] pos,
-                                                  long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPosText is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void native_drawPosText(long nativeCanvas,
-                                                  String text, float[] pos,
-                                                  long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPosText is not supported.", null, null /*data*/);
+            float x, float y, boolean isRtl, long paint, long typeface) {
+        drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
     }
 
     @LayoutlibDelegate
@@ -1064,7 +947,7 @@
                                                      int count, long path,
                                                      float hOffset,
                                                      float vOffset, int bidiFlags,
-                                                     long paint) {
+                                                     long paint, long typeface) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -1075,7 +958,8 @@
                                                      String text, long path,
                                                      float hOffset,
                                                      float vOffset,
-                                                     int flags, long paint) {
+                                                     int bidiFlags, long paint,
+                                                     long typeface) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -1095,6 +979,7 @@
         sManager.removeJavaReferenceFor(nativeCanvas);
     }
 
+
     // ---- Private delegate/helper methods ----
 
     /**
@@ -1132,6 +1017,41 @@
         canvasDelegate.mSnapshot.draw(drawable);
     }
 
+    private static void drawText(long nativeCanvas, final char[] text, final int index,
+            final int count, final float startX, final float startY, final boolean isRtl,
+            long paint, final long typeface) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                new GcSnapshot.Drawable() {
+            @Override
+            public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
+                // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+                // Any change to this method should be reflected in Paint.measureText
+
+                // assert that the typeface passed is actually the one stored in paint.
+                assert (typeface == paintDelegate.mNativeTypeface);
+
+                // Paint.TextAlign indicates how the text is positioned relative to X.
+                // LEFT is the default and there's nothing to do.
+                float x = startX;
+                int limit = index + count;
+                if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+                    RectF bounds = paintDelegate.measureText(text, index, count, null, 0,
+                            isRtl);
+                    float m = bounds.right - bounds.left;
+                    if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+                        x -= m / 2;
+                    } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+                        x -= m;
+                    }
+                }
+
+                new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x, startY)
+                        .renderText(index, limit, isRtl, null, 0, true);
+            }
+        });
+    }
+
     private Canvas_Delegate(Bitmap_Delegate bitmap) {
         mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 5adf4ca..24ef189 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -602,7 +602,7 @@
             return 0;
         }
 
-        RectF bounds = delegate.measureText(text, index, count, isRtl(bidiFlags));
+        RectF bounds = delegate.measureText(text, index, count, null, 0, bidiFlags);
         return bounds.right - bounds.left;
     }
 
@@ -618,11 +618,11 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
-            float maxWidth, int bidiFlags, float[] measuredWidth) {
+    /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, char[] text,
+            int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
 
         // get the delegate
-        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return 0;
         }
@@ -641,7 +641,7 @@
             }
 
             // measure from start to end
-            RectF bounds = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags));
+            RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
             float res = bounds.right - bounds.left;
 
             if (measuredWidth != null) {
@@ -660,10 +660,11 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
+    /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, String text,
+            boolean measureForwards,
             float maxWidth, int bidiFlags, float[] measuredWidth) {
-        return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
-                bidiFlags, measuredWidth);
+        return native_breakText(nativePaint, nativeTypeface, text.toCharArray(), 0, text.length(),
+                maxWidth, bidiFlags, measuredWidth);
     }
 
     @LayoutlibDelegate
@@ -950,8 +951,25 @@
     @LayoutlibDelegate
     /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
             char[] text, int index, int count, int bidiFlags, float[] widths) {
-        return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
-                index, count, bidiFlags, widths, 0);
+
+        if (widths != null) {
+            for (int i = 0; i< count; i++) {
+                widths[i]=0;
+            }
+        }
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            return 0;
+        }
+
+        // native_typeface is passed here since Framework's old implementation did not have the
+        // typeface object associated with the Paint. Since, we follow the new framework way,
+        // we store the typeface with the paint and use it directly.
+        assert (native_typeface == delegate.mNativeTypeface);
+
+        RectF bounds = delegate.measureText(text, index, count, widths, 0, bidiFlags);
+        return ((int) (bounds.right - bounds.left));
     }
 
     @LayoutlibDelegate
@@ -971,7 +989,7 @@
     @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
             char[] text, int index, int count, int contextIndex, int contextCount,
-            int flags, float[] advances, int advancesIndex) {
+            boolean isRtl, float[] advances, int advancesIndex) {
 
         if (advances != null)
             for (int i = advancesIndex; i< advancesIndex+count; i++)
@@ -987,25 +1005,21 @@
         // we store the typeface with the paint and use it directly.
         assert (native_typeface == delegate.mNativeTypeface);
 
-        boolean isRtl = isRtl(flags);
-
-        int limit = index + count;
-        RectF bounds = new BidiRenderer(null, delegate, text).renderText(
-                index, limit, isRtl, advances, advancesIndex, false, 0, 0);
+        RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, isRtl);
         return bounds.right - bounds.left;
     }
 
     @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
             String text, int start, int end, int contextStart, int contextEnd,
-            int flags, float[] advances, int advancesIndex) {
+            boolean isRtl, float[] advances, int advancesIndex) {
         // FIXME: support contextStart and contextEnd
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
         return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
-                contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
+                contextStart, contextEnd - contextStart, isRtl, advances, advancesIndex);
     }
 
     @LayoutlibDelegate
@@ -1062,7 +1076,7 @@
         // assert that the typeface passed is actually the one that we had stored.
         assert (native_typeface == delegate.mNativeTypeface);
 
-        delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
+        delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
     }
 
     @LayoutlibDelegate
@@ -1158,9 +1172,16 @@
         }
     }
 
-    /*package*/ RectF measureText(char[] text, int index, int count, boolean isRtl) {
-        return new BidiRenderer(null, this, text).renderText(
-                index, index + count, isRtl, null, 0, false, 0, 0);
+    /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
+            int advancesIndex, int bidiFlags) {
+        return new BidiRenderer(null, this, text)
+                .renderText(index, index + count, bidiFlags, advances, advancesIndex, false);
+    }
+
+    /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
+            int advancesIndex, boolean isRtl) {
+        return new BidiRenderer(null, this, text)
+                .renderText(index, index + count, isRtl, advances, advancesIndex, false);
     }
 
     private float getFontMetrics(FontMetrics metrics) {
@@ -1198,15 +1219,4 @@
             delegate.mFlags &= ~flagMask;
         }
     }
-
-    private static boolean isRtl(int flag) {
-        switch(flag) {
-        case Paint.BIDI_RTL:
-        case Paint.BIDI_FORCE_RTL:
-        case Paint.BIDI_DEFAULT_RTL:
-            return true;
-        default:
-            return false;
-        }
-    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 0ec7115..7153a90 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -297,14 +297,15 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_arcTo(long nPath, RectF oval,
+    /*package*/ static void native_arcTo(long nPath, float left, float top, float right,
+            float bottom,
                     float startAngle, float sweepAngle, boolean forceMoveTo) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
             return;
         }
 
-        pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
+        pathDelegate.arcTo(left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
     }
 
     @LayoutlibDelegate
@@ -353,8 +354,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_addArc(long nPath, RectF oval,
-            float startAngle, float sweepAngle) {
+    /*package*/ static void native_addArc(long nPath, float left, float top, float right,
+            float bottom, float startAngle, float sweepAngle) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
             return;
@@ -362,13 +363,13 @@
 
         // because x/y is the center of the circle, need to offset this by the radius
         pathDelegate.mPath.append(new Arc2D.Float(
-                oval.left, oval.top, oval.width(), oval.height(),
+                left, top, right - left, bottom - top,
                 -startAngle, -sweepAngle, Arc2D.OPEN), false);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_addRoundRect(
-            long nPath, RectF rect, float rx, float ry, int dir) {
+    /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+            float bottom, float rx, float ry, int dir) {
 
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
@@ -376,14 +377,15 @@
         }
 
         pathDelegate.mPath.append(new RoundRectangle2D.Float(
-                rect.left, rect.top, rect.width(), rect.height(), rx * 2, ry * 2), false);
+                left, top, right - left, bottom - top, rx * 2, ry * 2), false);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void native_addRoundRect(long nPath, RectF rect, float[] radii, int dir) {
+    /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+            float bottom, float[] radii, int dir) {
         // Java2D doesn't support different rounded corners in each corner, so just use the
         // first value.
-        native_addRoundRect(nPath, rect, radii[0], radii[1], dir);
+        native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
 
         // there can be a case where this API is used but with similar values for all corners, so
         // in that case we don't warn.
@@ -710,14 +712,19 @@
      * start of the arc. However, if the path is empty, then we call moveTo()
      * with the first point of the arc. The sweep angle is tread mod 360.
      *
-     * @param oval        The bounds of oval defining shape and size of the arc
+     * @param left        The left of oval defining shape and size of the arc
+     * @param top         The top of oval defining shape and size of the arc
+     * @param right       The right of oval defining shape and size of the arc
+     * @param bottom      The bottom of oval defining shape and size of the arc
      * @param startAngle  Starting angle (in degrees) where the arc begins
      * @param sweepAngle  Sweep angle (in degrees) measured clockwise, treated
      *                    mod 360.
      * @param forceMoveTo If true, always begin a new contour with the arc
      */
-    private void arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) {
-        Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), -startAngle,
+    private void arcTo(float left, float top, float right, float bottom, float startAngle,
+            float sweepAngle,
+            boolean forceMoveTo) {
+        Arc2D arc = new Arc2D.Float(left, top, right - left, bottom - top, -startAngle,
                 -sweepAngle, Arc2D.OPEN);
         mPath.append(arc, true /*connect*/);
 
diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
index 973fa0e..6247dae 100644
--- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
@@ -37,14 +37,17 @@
 
         switch (dir) {
         case 0: // Layout.DIR_REQUEST_LTR
-        case 1: // Layout.DIR_REQUEST_RTL
-            break;  // No change.
-        case -1:
-            dir = Bidi.LEVEL_DEFAULT_LTR;
+            dir = Bidi.LTR;
             break;
-        case -2:
+        case 1: // Layout.DIR_REQUEST_RTL
+            dir = Bidi.RTL;
+            break;
+        case -1: // Layout.DIR_REQUEST_DEFAULT_RTL
             dir = Bidi.LEVEL_DEFAULT_RTL;
             break;
+        case -2: // Layout.DIR_REQUEST_DEFAULT_LTR
+            dir = Bidi.LEVEL_DEFAULT_LTR;
+            break;
         default:
             // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue.
             Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index d32f6ee..997b199 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -47,8 +47,8 @@
     }
 
     @Override
-    public void resized(Rect arg1, Rect arg1p5, Rect arg2, Rect arg3,
-            boolean arg4, Configuration arg5) throws RemoteException {
+    public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, boolean b,
+            Configuration configuration) throws RemoteException {
         // pass for now.
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index e1064b1..130500a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -85,10 +85,11 @@
         // pass for now.
         return false;
     }
+
     @Override
-    public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
-            int arg4_5, Rect arg5Z, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b,
-            Surface arg8) throws RemoteException {
+    public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
+            int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
+            Configuration configuration, Surface surface) throws RemoteException {
         // pass for now.
         return 0;
     }
diff --git a/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java b/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java
new file mode 100644
index 0000000..36efc3a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -0,0 +1,78 @@
+/*
+ * 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 dalvik.system;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide implementation of a select few native methods of {@link VMRuntime}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of VMRuntime have been replaced
+ * by calls to methods of the same name in this delegate class.
+ */
+public class VMRuntime_Delegate {
+
+    // Copied from libcore/libdvm/src/main/java/dalvik/system/VMRuntime
+    @LayoutlibDelegate
+    /*package*/ static Object newUnpaddedArray(VMRuntime runtime, Class<?> componentType,
+            int minLength) {
+        // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
+        // allocations are 8byte aligned so having 4bytes of array data avoids padding.
+        if (!componentType.isPrimitive()) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return java.lang.reflect.Array.newInstance(componentType, size);
+        } else if (componentType == char.class) {
+            int bytes = 20 + (2 * minLength);
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes / 2;
+            return new char[size];
+        } else if (componentType == int.class) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return new int[size];
+        } else if (componentType == byte.class) {
+            int bytes = 20 + minLength;
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes;
+            return new byte[size];
+        } else if (componentType == boolean.class) {
+            int bytes = 20 + minLength;
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes;
+            return new boolean[size];
+        } else if (componentType == short.class) {
+            int bytes = 20 + (2 * minLength);
+            int alignedUpBytes = (bytes + 7) & -8;
+            int dataBytes = alignedUpBytes - 20;
+            int size = dataBytes / 2;
+            return new short[size];
+        } else if (componentType == float.class) {
+            int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+            return new float[size];
+        } else if (componentType == long.class) {
+            return new long[minLength];
+        } else if (componentType == double.class) {
+            return new double[minLength];
+        } else {
+            assert componentType == void.class;
+            throw new IllegalArgumentException("Can't allocate an array of void");
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index 19d249b..71947b0 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -148,12 +148,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static String getISO3CountryNative(String locale) {
+    /*package*/ static String getISO3Country(String locale) {
         return "";
     }
 
     @LayoutlibDelegate
-    /*package*/ static String getISO3LanguageNative(String locale) {
+    /*package*/ static String getISO3Language(String locale) {
         return "";
     }
 
@@ -184,11 +184,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static String languageTagForLocale(String locale) {
-        return "";
-    }
-
-    @LayoutlibDelegate
     /*package*/ static boolean initLocaleDataNative(String locale, LocaleData result) {
 
         // Used by Calendar.
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index 274516c..8b362ec 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -41,27 +41,29 @@
  */
 public class TestDelegates extends TestCase {
 
+    private List<String> mErrors = new ArrayList<String>();
+
     public void testNativeDelegates() {
 
         final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
-        final int count = classes.length;
-        for (int i = 0 ; i < count ; i++) {
-            loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+        mErrors.clear();
+        for (String clazz : classes) {
+            loadAndCompareClasses(clazz, clazz + "_Delegate");
         }
+        assertTrue(getErrors(), mErrors.isEmpty());
     }
 
     public void testMethodDelegates() {
         final String[] methods = CreateInfo.DELEGATE_METHODS;
-        final int count = methods.length;
-        for (int i = 0 ; i < count ; i++) {
-            String methodName = methods[i];
-
+        mErrors.clear();
+        for (String methodName : methods) {
             // extract the class name
             String className = methodName.substring(0, methodName.indexOf('#'));
             String targetClassName = className.replace('$', '_') + "_Delegate";
 
             loadAndCompareClasses(className, targetClassName);
         }
+        assertTrue(getErrors(), mErrors.isEmpty());
     }
 
     private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
@@ -73,9 +75,9 @@
 
             compare(originalClass, delegateClass);
         } catch (ClassNotFoundException e) {
-           fail("Failed to load class: " + e.getMessage());
+            mErrors.add("Failed to load class: " + e.getMessage());
         } catch (SecurityException e) {
-            fail("Failed to load class: " + e.getMessage());
+            mErrors.add("Failed to load class: " + e.getMessage());
         }
     }
 
@@ -122,35 +124,39 @@
                         parameters);
 
                 // check the return type of the methods match.
-                assertTrue(
-                        String.format("Delegate method %1$s.%2$s does not match the corresponding " +
-                                "framework method which returns %3$s",
-                                delegateClass.getName(),
-                                getMethodName(delegateMethod),
-                                originalMethod.getReturnType().getName()),
-                        delegateMethod.getReturnType() == originalMethod.getReturnType());
+                if (delegateMethod.getReturnType() != originalMethod.getReturnType()) {
+                    mErrors.add(
+                            String.format("Delegate method %1$s.%2$s does not match the " +
+                                    "corresponding framework method which returns %3$s",
+                            delegateClass.getName(),
+                            getMethodName(delegateMethod),
+                            originalMethod.getReturnType().getName()));
+                }
 
                 // check that the method has the annotation
-                assertNotNull(
-                        String.format(
-                                "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation",
-                                delegateMethod.getName(),
-                                originalClass.getName()),
-                        delegateMethod.getAnnotation(LayoutlibDelegate.class));
+                if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+                    mErrors.add(
+                            String.format("Delegate method %1$s for class %2$s does not have the " +
+                                            "@LayoutlibDelegate annotation",
+                                    delegateMethod.getName(),
+                                    originalClass.getName()));
+                }
 
                 // check that the method is static
-                assertTrue(
-                        String.format(
-                                "Delegate method %1$s for class %2$s is not static",
-                                delegateMethod.getName(),
-                                originalClass.getName()),
-                        (delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
+                if ((delegateMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
+                    mErrors.add(
+                            String.format(
+                                    "Delegate method %1$s for class %2$s is not static",
+                                    delegateMethod.getName(),
+                                    originalClass.getName())
+                    );
+                }
 
                 // add the method as checked.
                 checkedDelegateMethods.add(delegateMethod);
             } catch (NoSuchMethodException e) {
                 String name = getMethodName(originalMethod, parameters);
-                fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
+                mErrors.add(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
             }
         }
 
@@ -167,12 +173,12 @@
                 continue;
             }
 
-            assertTrue(
-                    String.format(
-                            "Delegate method %1$s.%2$s is not used anymore and must be removed",
-                            delegateClass.getName(),
-                            getMethodName(delegateMethod)),
-                    checkedDelegateMethods.contains(delegateMethod));
+            if (!checkedDelegateMethods.contains(delegateMethod)) {
+                mErrors.add(String.format(
+                        "Delegate method %1$s.%2$s is not used anymore and must be removed",
+                        delegateClass.getName(),
+                        getMethodName(delegateMethod)));
+            }
         }
 
     }
@@ -203,4 +209,12 @@
 
         return sb.toString();
     }
+
+    private String getErrors() {
+        StringBuilder s = new StringBuilder();
+        for (String error : mErrors) {
+            s.append(error).append('\n');
+        }
+        return s.toString();
+    }
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 1f7a28e..552fb6c 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -152,6 +152,7 @@
         "com.android.internal.util.XmlUtils#convertValueToInt",
         "com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
         "android.os.SystemProperties#native_get",
+        "dalvik.system.VMRuntime#newUnpaddedArray"
     };
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 59b48e4..31900ecd7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -49,6 +49,10 @@
     /** {@hide} */
     public static final String hiddenSSIDVarName = "scan_ssid";
     /** {@hide} */
+    public static final String pmfVarName = "ieee80211w";
+    /** {@hide} */
+    public static final String updateIdentiferVarName = "update_identifier";
+    /** {@hide} */
     public static final int INVALID_NETWORK_ID = -1;
     /**
      * Recognized key management schemes.
@@ -264,6 +268,18 @@
     public boolean hiddenSSID;
 
     /**
+     * This is a network that requries Protected Management Frames (PMF).
+     * @hide
+     */
+    public boolean requirePMF;
+
+    /**
+     * Update identifier, for Passpoint network.
+     * @hide
+     */
+    public String updateIdentifier;
+
+    /**
      * The set of key management protocols supported by this configuration.
      * See {@link KeyMgmt} for descriptions of the values.
      * Defaults to WPA-PSK WPA-EAP.
@@ -1155,6 +1171,8 @@
         dest.writeInt(wepTxKeyIndex);
         dest.writeInt(priority);
         dest.writeInt(hiddenSSID ? 1 : 0);
+        dest.writeInt(requirePMF ? 1 : 0);
+        dest.writeString(updateIdentifier);
 
         writeBitSet(dest, allowedKeyManagement);
         writeBitSet(dest, allowedProtocols);
@@ -1196,6 +1214,9 @@
                 config.wepTxKeyIndex = in.readInt();
                 config.priority = in.readInt();
                 config.hiddenSSID = in.readInt() != 0;
+                config.requirePMF = in.readInt() != 0;
+                config.updateIdentifier = in.readString();
+
                 config.allowedKeyManagement   = readBitSet(in);
                 config.allowedProtocols       = readBitSet(in);
                 config.allowedAuthAlgorithms  = readBitSet(in);
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index e7e6767..ddca85e 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -221,12 +221,22 @@
             return key;
         }
 
-        private Object getListener(int key, boolean force) {
-            Log.d(TAG, "getListener() key=" + key + " force=" + force);
+        private Object peekListener(int key) {
+            Log.d(TAG, "peekListener() key=" + key);
             if (key == INVALID_LISTENER_KEY)
                 return null;
             synchronized (mListenerMapLock) {
-                if (!force) {
+                return mListenerMap.get(key);
+            }
+        }
+
+
+        private Object getListener(int key, boolean forceRemove) {
+            Log.d(TAG, "getListener() key=" + key + " force=" + forceRemove);
+            if (key == INVALID_LISTENER_KEY)
+                return null;
+            synchronized (mListenerMapLock) {
+                if (!forceRemove) {
                     int count = mListenerMapCount.get(key);
                     Log.d(TAG, "count=" + count);
                     mListenerMapCount.put(key, --count);
@@ -322,7 +332,7 @@
                         break;
 
                     case START_OSU_BROWSER:
-                        listener = getListener(message.arg2, true);
+                        listener = peekListener(message.arg2);
                         if (listener != null) {
                             ParcelableString str = (ParcelableString) message.obj;
                             if (str == null || str.string == null)