Merge "Add 'pm' operation to set a package's app-linking state" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 21ca33c..79d5ea0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28898,6 +28898,7 @@
     method public java.lang.String getCallingPackage();
     method public int getCallingUid();
     method public android.os.Bundle getExtras();
+    method public boolean isActive();
   }
 
   public abstract class VoiceInteractionSessionService extends android.app.Service {
diff --git a/api/system-current.txt b/api/system-current.txt
index bbff8c0..0af72d5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -31050,6 +31050,7 @@
     method public java.lang.String getCallingPackage();
     method public int getCallingUid();
     method public android.os.Bundle getExtras();
+    method public boolean isActive();
   }
 
   public abstract class VoiceInteractionSessionService extends android.app.Service {
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
index fc70bc60..ba00134 100644
--- a/core/java/android/content/res/DrawableCache.java
+++ b/core/java/android/content/res/DrawableCache.java
@@ -52,6 +52,6 @@
 
     @Override
     public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
-        return false;
+        return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
     }
 }
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index b039fc7..a2c1d18 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -103,8 +103,12 @@
     }
 
     @Override public boolean onTextContextMenuItem(int id) {
-        // Select all shouldn't be handled by the original edit text, but by the extracted one.
-        if (id != android.R.id.selectAll && mIME != null && mIME.onExtractTextContextMenuItem(id)) {
+        // Select all and Replace text shouldn't be handled by the original edit text, but by the
+        // extracted one.
+        if (id == android.R.id.selectAll || id == android.R.id.replaceText) {
+            return super.onTextContextMenuItem(id);
+        }
+        if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
             // Mode was started on Extracted, needs to be stopped here.
             // Cut will change the text, which stops selection mode.
             if (id == android.R.id.copy || id == android.R.id.paste) stopTextActionMode();
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index a3ccbd3..f08e93c 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -322,6 +322,22 @@
             return mExtras;
         }
 
+        /**
+         * Check whether this request is currently active.  A request becomes inactive after
+         * calling {@link #cancel} or a final result method that completes the request.  After
+         * this point, further interactions with the request will result in
+         * {@link java.lang.IllegalStateException} errors; you should not catch these errors,
+         * but can use this method if you need to determine the state of the request.  Returns
+         * true if the request is still active.
+         */
+        public boolean isActive() {
+            VoiceInteractionSession session = mSession.get();
+            if (session == null) {
+                return false;
+            }
+            return session.isRequestActive(mInterface.asBinder());
+        }
+
         void finishRequest() {
             VoiceInteractionSession session = mSession.get();
             if (session == null) {
@@ -338,6 +354,7 @@
 
         /**
          * Ask the app to cancel this current request.
+         * This also finishes the request (it is no longer active).
          */
         public void cancel() {
             try {
@@ -389,6 +406,7 @@
          * in a call to
          * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult
          * VoiceInteractor.ConfirmationRequest.onConfirmationResult}.
+         * This finishes the request (it is no longer active).
          */
         public void sendConfirmationResult(boolean confirmed, Bundle result) {
             try {
@@ -475,6 +493,7 @@
          * and resulting in a call to
          * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
          * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
+         * This finishes the request (it is no longer active).
          */
         public void sendPickOptionResult(
                 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
@@ -522,6 +541,7 @@
          * in a call to
          * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult
          * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}.
+         * This finishes the request (it is no longer active).
          */
         public void sendCompleteResult(Bundle result) {
             try {
@@ -570,7 +590,8 @@
          * Report that the voice interactor has finished aborting the voice operation, resulting
          * in a call to
          * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult
-         * VoiceInteractor.AbortVoiceRequest.onAbortResult}.
+         * VoiceInteractor.AbortVoiceRequest.onAbortResult}.  This finishes the request (it
+         * is no longer active).
          */
         public void sendAbortResult(Bundle result) {
             try {
@@ -630,6 +651,7 @@
          * Report the final result of the request, completing the request and resulting in a call to
          * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
          * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted.
+         * This finishes the request (it is no longer active).
          */
         public void sendResult(Bundle result) {
             sendCommandResult(true, result);
@@ -820,7 +842,15 @@
     }
 
     void addRequest(Request req) {
-        mActiveRequests.put(req.mInterface.asBinder(), req);
+        synchronized (this) {
+            mActiveRequests.put(req.mInterface.asBinder(), req);
+        }
+    }
+
+    boolean isRequestActive(IBinder reqInterface) {
+        synchronized (this) {
+            return mActiveRequests.containsKey(reqInterface);
+        }
     }
 
     Request removeRequest(IBinder reqInterface) {
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index 7ea9da1..5c088cd 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -173,7 +173,7 @@
 
         /**
          * Specifies whether the BidiFormatter to be built should also "reset" directionality before
-         * a string being bidi-wrapped, not just after it. The default is false.
+         * a string being bidi-wrapped, not just after it. The default is true.
          */
         public Builder stereoReset(boolean stereoReset) {
             if (stereoReset) {
@@ -363,12 +363,13 @@
      * If {@code isolate}, directionally isolates the string so that it does not garble its
      * surroundings. Currently, this is done by "resetting" the directionality after the string by
      * appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when
-     * either the overall directionality or the exit directionality of the string is opposite to that
-     * of the context. If the formatter was built using {@link Builder#stereoReset(boolean)} and
-     * passing "true" as an argument, also prepends a Unicode bidi mark matching the context
-     * directionality when either the overall directionality or the entry directionality of the
-     * string is opposite to that of the context. Note that as opposed to the overall
-     * directionality, the entry and exit directionalities are determined from the string itself.
+     * either the overall directionality or the exit directionality of the string is opposite to
+     * that of the context. Unless the formatter was built using
+     * {@link Builder#stereoReset(boolean)} with a {@code false} argument, also prepends a Unicode
+     * bidi mark matching the context directionality when either the overall directionality or the
+     * entry directionality of the string is opposite to that of the context. Note that as opposed
+     * to the overall directionality, the entry and exit directionalities are determined from the
+     * string itself.
      * <p>
      * Does *not* do HTML-escaping.
      *
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 605b91d..39e8694 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -741,11 +741,11 @@
 
         if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
             if (mCharsValid) {
-                ret = wp.getRunAdvance(mChars, start, contextEnd, contextStart, contextEnd,
+                ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd,
                         runIsRtl, end);
             } else {
                 int delta = mStart;
-                ret = wp.getRunAdvance(mText, delta + start, delta + contextEnd,
+                ret = wp.getRunAdvance(mText, delta + start, delta + end,
                         delta + contextStart, delta + contextEnd, runIsRtl, delta + end);
             }
         }
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 82689b9..b5068b2 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -16,12 +16,18 @@
 
 package android.text.format;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
+import android.text.BidiFormatter;
+import android.text.TextUtils;
+import android.view.View;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
 
+import java.util.Locale;
+
 /**
  * Utility class to aid in formatting common values that are not covered
  * by the {@link java.util.Formatter} class in {@link java.util}
@@ -46,8 +52,23 @@
         }
     }
 
+    /* Wraps the source string in bidi formatting characters in RTL locales */
+    private static String bidiWrap(@NonNull Context context, String source) {
+        final Locale locale = context.getResources().getConfiguration().locale;
+        if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) {
+            return BidiFormatter.getInstance(true /* RTL*/).unicodeWrap(source);
+        } else {
+            return source;
+        }
+    }
+
     /**
-     * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc
+     * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc.
+     *
+     * If the context has a right-to-left locale, the returned string is wrapped in bidi formatting
+     * characters to make sure it's displayed correctly if inserted inside a right-to-left string.
+     * (This is useful in cases where the unit strings, like "MB", are left-to-right, but the
+     * locale is right-to-left.)
      *
      * @param context Context to use to load the localized units
      * @param sizeBytes size value to be formatted, in bytes
@@ -58,8 +79,8 @@
             return "";
         }
         final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0);
-        return context.getString(com.android.internal.R.string.fileSizeSuffix,
-                res.value, res.units);
+        return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
+                res.value, res.units));
     }
 
     /**
@@ -71,8 +92,8 @@
             return "";
         }
         final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER);
-        return context.getString(com.android.internal.R.string.fileSizeSuffix,
-                res.value, res.units);
+        return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
+                res.value, res.units));
     }
 
     /** {@hide} */
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index b2b98db..ed858e7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1556,13 +1556,6 @@
         if (accessibilityId == getAccessibilityViewId()) {
             return this;
         }
-        // If the data changed the children are invalid since the data model changed.
-        // Hence, we pretend they do not exist. After a layout the children will sync
-        // with the model at which point we notify that the accessibility state changed,
-        // so a service will be able to re-fetch the views.
-        if (mDataChanged) {
-            return null;
-        }
         return super.findViewByAccessibilityIdTraversal(accessibilityId);
     }
 
@@ -2409,18 +2402,6 @@
 
     class ListItemAccessibilityDelegate extends AccessibilityDelegate {
         @Override
-        public AccessibilityNodeInfo createAccessibilityNodeInfo(View host) {
-            // If the data changed the children are invalid since the data model changed.
-            // Hence, we pretend they do not exist. After a layout the children will sync
-            // with the model at which point we notify that the accessibility state changed,
-            // so a service will be able to re-fetch the views.
-            if (mDataChanged) {
-                return null;
-            }
-            return super.createAccessibilityNodeInfo(host);
-        }
-
-        @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d897f49..15d13ae 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3225,7 +3225,8 @@
         }
 
         private void updateReplaceItem(Menu menu) {
-            boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions();
+            boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions()
+                    && !(mTextView.isInExtractedMode() && mTextView.hasSelection());
             boolean replaceItemExists = menu.findItem(TextView.ID_REPLACE) != null;
             if (canReplace && !replaceItemExists) {
                 menu.add(Menu.NONE, TextView.ID_REPLACE, MENU_ITEM_ORDER_REPLACE,
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index ff989bd..0910daf 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -49,8 +49,10 @@
     public boolean showSessionForActiveService(Bundle args, int sourceFlags,
             IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
         try {
-            return mVoiceInteractionManagerService.showSessionForActiveService(args, sourceFlags,
-                    showCallback, activityToken);
+            if (mVoiceInteractionManagerService != null) {
+                return mVoiceInteractionManagerService.showSessionForActiveService(args,
+                        sourceFlags, showCallback, activityToken);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call showSessionForActiveService", e);
         }
@@ -59,7 +61,9 @@
 
     public void launchVoiceAssistFromKeyguard() {
         try {
-            mVoiceInteractionManagerService.launchVoiceAssistFromKeyguard();
+            if (mVoiceInteractionManagerService != null) {
+                mVoiceInteractionManagerService.launchVoiceAssistFromKeyguard();
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call launchVoiceAssistFromKeyguard", e);
         }
@@ -87,7 +91,11 @@
 
     public ComponentName getActiveServiceComponentName() {
         try {
-            return mVoiceInteractionManagerService.getActiveServiceComponentName();
+            if (mVoiceInteractionManagerService != null) {
+                return mVoiceInteractionManagerService.getActiveServiceComponentName();
+            } else {
+                return null;
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call getActiveServiceComponentName", e);
             return null;
@@ -106,7 +114,9 @@
 
     public void hideCurrentSession() {
         try {
-            mVoiceInteractionManagerService.hideCurrentSession();
+            if (mVoiceInteractionManagerService != null) {
+                mVoiceInteractionManagerService.hideCurrentSession();
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call hideCurrentSession", e);
         }
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 3f5e300..3c3a8a8 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -22,7 +22,9 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:orientation="horizontal"
-                android:padding="@dimen/timepicker_separator_padding">
+                android:padding="@dimen/timepicker_separator_padding"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp">
 
     <!-- The hour should always be to the left of the separator,
          regardless of the current locale's layout direction. -->
@@ -68,14 +70,14 @@
         android:layout_height="wrap_content"
         android:layout_toRightOf="@+id/minutes"
         android:layout_alignBaseline="@+id/minutes"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp"
         android:orientation="vertical"
         android:baselineAlignedChildIndex="1">
         <CheckedTextView
             android:id="@+id/am_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
-            android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingTop="@dimen/timepicker_am_top_padding"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
@@ -84,8 +86,6 @@
             android:id="@+id/pm_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
-            android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingTop="@dimen/timepicker_pm_top_padding"
             android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a855f2f..06b6389 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2261,4 +2261,12 @@
  
     <!-- How long history of previous vibrations should be kept for the dumpsys. -->
     <integer name="config_previousVibrationsDumpLimit">20</integer>
+
+    <!-- Number of retries Cell Data should attempt for a given error code before
+         restarting the modem.
+         Error codes not listed will not lead to modem restarts.
+         Array of "code#,retry#"  -->
+    <string-array name="config_cell_retries_per_error_code">
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index b980777..558afa7 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -127,7 +127,7 @@
     <!-- Text size for the time picker header HH:MM label. This value is large
          enough that we don't need to use scaled pixels, dp is fine. -->
     <dimen name="timepicker_time_label_size">60dp</dimen>
-    <dimen name="timepicker_ampm_label_size">16sp</dimen>
+    <dimen name="timepicker_ampm_label_size">16dp</dimen>
     <dimen name="timepicker_ampm_horizontal_padding">16dp</dimen>
     <dimen name="timepicker_am_top_padding">4dp</dimen>
     <dimen name="timepicker_pm_top_padding">4dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3695cf6..f590ebe 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2311,4 +2311,6 @@
 
   <java-symbol type="bool" name="allow_stacked_button_bar" />
   <java-symbol type="bool" name="config_eap_sim_based_auth_supported" />
+
+  <java-symbol type="array" name="config_cell_retries_per_error_code" />
 </resources>
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index aa5fde3..8091421 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -33,4 +33,7 @@
     /** Used for Notification sound playback. */
     void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa);
     void stopAsync();
+
+    /** Return the title of the media. */
+    String getTitle(in Uri uri);
 }
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index faeebe6..9e9d602 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.provider.MediaStore.MediaColumns;
 import android.util.Log;
 
 import java.io.IOException;
@@ -50,6 +51,8 @@
         MediaStore.Audio.Media.DATA,
         MediaStore.Audio.Media.TITLE
     };
+    /** Selection that limits query results to just audio files */
+    private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%'";
 
     // keep references on active Ringtones until stopped or completion listener called.
     private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
@@ -193,11 +196,14 @@
      */
     public String getTitle(Context context) {
         if (mTitle != null) return mTitle;
-        return mTitle = getTitle(context, mUri, true);
+        return mTitle = getTitle(context, mUri, true /*followSettingsUri*/, mAllowRemote);
     }
 
-    private static String getTitle(Context context, Uri uri, boolean followSettingsUri) {
-        Cursor cursor = null;
+    /**
+     * @hide
+     */
+    public static String getTitle(
+            Context context, Uri uri, boolean followSettingsUri, boolean allowRemote) {
         ContentResolver res = context.getContentResolver();
         
         String title = null;
@@ -209,31 +215,45 @@
                 if (followSettingsUri) {
                     Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context,
                             RingtoneManager.getDefaultType(uri));
-                    String actualTitle = getTitle(context, actualUri, false);
+                    String actualTitle = getTitle(
+                            context, actualUri, false /*followSettingsUri*/, allowRemote);
                     title = context
                             .getString(com.android.internal.R.string.ringtone_default_with_actual,
                                     actualTitle);
                 }
             } else {
+                Cursor cursor = null;
                 try {
                     if (MediaStore.AUTHORITY.equals(authority)) {
-                        cursor = res.query(uri, MEDIA_COLUMNS, null, null, null);
+                        final String mediaSelection = allowRemote ? null : MEDIA_SELECTION;
+                        cursor = res.query(uri, MEDIA_COLUMNS, mediaSelection, null, null);
+                        if (cursor != null && cursor.getCount() == 1) {
+                            cursor.moveToFirst();
+                            return cursor.getString(2);
+                        }
+                        // missing cursor is handled below
                     }
                 } catch (SecurityException e) {
-                    // missing cursor is handled below
-                }
-
-                try {
-                    if (cursor != null && cursor.getCount() == 1) {
-                        cursor.moveToFirst();
-                        return cursor.getString(2);
-                    } else {
-                        title = uri.getLastPathSegment();
+                    IRingtonePlayer mRemotePlayer = null;
+                    if (allowRemote) {
+                        AudioManager audioManager =
+                                (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+                        mRemotePlayer = audioManager.getRingtonePlayer();
+                    }
+                    if (mRemotePlayer != null) {
+                        try {
+                            title = mRemotePlayer.getTitle(uri);
+                        } catch (RemoteException re) {
+                        }
                     }
                 } finally {
                     if (cursor != null) {
                         cursor.close();
                     }
+                    cursor = null;
+                }
+                if (title == null) {
+                    title = uri.getLastPathSegment();
                 }
             }
         }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 35037bb..86e8560 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -385,13 +385,14 @@
             trackIdSet.clear();
 
             // TODO: Validate the track list.
+            final List<TvTrackInfo> tracksCopy = new ArrayList<>(tracks);
             executeOrPostRunnable(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (DEBUG) Log.d(TAG, "notifyTracksChanged");
                         if (mSessionCallback != null) {
-                            mSessionCallback.onTracksChanged(tracks);
+                            mSessionCallback.onTracksChanged(tracksCopy);
                         }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyTracksChanged", e);
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index 4526af5..f699fce 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -31,7 +31,7 @@
         android:layout_height="match_parent"
         />
 
-    <LinearLayout
+    <com.android.keyguard.AlphaOptimizedLinearLayout
         android:id="@+id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
@@ -63,6 +63,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             />
-    </LinearLayout>
+    </com.android.keyguard.AlphaOptimizedLinearLayout>
 
 </com.android.systemui.statusbar.NotificationOverflowContainer>
diff --git a/packages/SystemUI/src/com/android/systemui/DejankUtils.java b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
new file mode 100644
index 0000000..fc98ec4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.StrictMode;
+import android.view.Choreographer;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class for methods used to dejank the UI.
+ */
+public class DejankUtils {
+
+    private static final Choreographer sChoreographer = Choreographer.getInstance();
+    private static final Handler sHandler = new Handler();
+
+    private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>();
+
+    private static final Runnable sAnimationCallbackRunnable = new Runnable() {
+        @Override
+        public void run() {
+            for (int i = 0; i < sPendingRunnables.size(); i++) {
+                sHandler.post(sPendingRunnables.get(i));
+            }
+            sPendingRunnables.clear();
+        }
+    };
+
+    /**
+     * Executes {@code r} after performTraversals. Use this do to CPU heavy work for which the
+     * timing is not critical for animation. The work is then scheduled at the same time
+     * RenderThread is doing its thing, leading to better parallelization.
+     *
+     * <p>Needs to be called from the main thread.
+     */
+    public static void postAfterTraversal(Runnable r) {
+        throwIfNotCalledOnMainThread();
+        sPendingRunnables.add(r);
+        postAnimationCallback();
+    }
+
+    /**
+     * Removes a previously scheduled runnable.
+     *
+     * <p>Needs to be called from the main thread.
+     */
+    public static void removeCallbacks(Runnable r) {
+        throwIfNotCalledOnMainThread();
+        sPendingRunnables.remove(r);
+        sHandler.removeCallbacks(r);
+    }
+
+    private static void postAnimationCallback() {
+        sChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, sAnimationCallbackRunnable,
+                null);
+    }
+
+    private static void throwIfNotCalledOnMainThread() {
+        if (!Looper.getMainLooper().isCurrentThread()) {
+            throw new IllegalStateException("should be called from the main thread.");
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index e9a256c..fe876d7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -171,6 +171,13 @@
             }
             mAsyncPlayer.stop();
         }
+
+        @Override
+        public String getTitle(Uri uri) {
+            final UserHandle user = Binder.getCallingUserHandle();
+            return Ringtone.getTitle(getContextForUser(user), uri,
+                    false /*followSettingsUri*/, false /*allowRemote*/);
+        }
     };
 
     private Context getContextForUser(UserHandle user) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index ddde106..7b83e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -23,10 +23,9 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.qs.UsageTracker;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.UsageTracker;
 import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
 /** Quick settings tile: Hotspot **/
 public class HotspotTile extends QSTile<QSTile.BooleanState> {
@@ -37,14 +36,12 @@
     private final HotspotController mController;
     private final Callback mCallback = new Callback();
     private final UsageTracker mUsageTracker;
-    private final KeyguardMonitor mKeyguard;
 
     public HotspotTile(Host host) {
         super(host);
         mController = host.getHotspotController();
         mUsageTracker = newUsageTracker(host.getContext());
         mUsageTracker.setListening(true);
-        mKeyguard = host.getKeyguardMonitor();
     }
 
     @Override
@@ -97,7 +94,7 @@
         if (arg instanceof Boolean) {
             state.value = (boolean) arg;
         } else {
-            mController.isHotspotEnabled();
+            state.value = mController.isHotspotEnabled();
         }
         state.icon = state.visible && state.value ? mEnable : mDisable;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 7cde44c..403af70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -656,12 +656,14 @@
     }
 
     private void setContentAlpha(float contentAlpha) {
-        int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
-                : LAYER_TYPE_HARDWARE;
         View contentView = getContentView();
-        int currentLayerType = contentView.getLayerType();
-        if (currentLayerType != layerType) {
-            contentView.setLayerType(layerType, null);
+        if (contentView.hasOverlappingRendering()) {
+            int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
+                    : LAYER_TYPE_HARDWARE;
+            int currentLayerType = contentView.getLayerType();
+            if (currentLayerType != layerType) {
+                contentView.setLayerType(layerType, null);
+            }
         }
         contentView.setAlpha(contentAlpha);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index a7afec4..e9b2c61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -29,6 +29,7 @@
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.R;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.DejankUtils;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -46,7 +47,6 @@
     private KeyguardHostView mKeyguardView;
     private ViewGroup mRoot;
     private boolean mShowingSoon;
-    private Choreographer mChoreographer = Choreographer.getInstance();
     private int mBouncerPromptReason;
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
@@ -70,16 +70,13 @@
             return;
         }
 
-        mBouncerPromptReason = mCallback.getBouncerPromptReason();
-
         // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole
         // Keyguard. If we need to authenticate, show the bouncer.
         if (!mKeyguardView.dismiss()) {
             mShowingSoon = true;
 
             // Split up the work over multiple frames.
-            mChoreographer.postCallbackDelayed(Choreographer.CALLBACK_ANIMATION, mShowRunnable,
-                    null, 16);
+            DejankUtils.postAfterTraversal(mShowRunnable);
         }
     }
 
@@ -107,7 +104,7 @@
     }
 
     private void cancelShowRunnable() {
-        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mShowRunnable, null);
+        DejankUtils.removeCallbacks(mShowRunnable);
         mShowingSoon = false;
     }
 
@@ -165,6 +162,7 @@
         if (wasInitialized) {
             mKeyguardView.showPrimarySecurityScreen();
         }
+        mBouncerPromptReason = mCallback.getBouncerPromptReason();
     }
 
     private void ensureView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f40f5016..416fb36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,6 +40,7 @@
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
@@ -184,6 +185,15 @@
         mBarTransitions = new NavigationBarTransitions(this);
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        ViewRootImpl root = getViewRootImpl();
+        if (root != null) {
+            root.setDrawDuringWindowsAnimating(true);
+        }
+    }
+
     public BarTransitions getBarTransitions() {
         return mBarTransitions;
     }
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 f0927a8..2248905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2677,6 +2677,9 @@
         if (mBluetoothController != null) {
             mBluetoothController.dump(fd, pw, args);
         }
+        if (mHotspotController != null) {
+            mHotspotController.dump(fd, pw, args);
+        }
         if (mCastController != null) {
             mCastController.dump(fd, pw, args);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 6d04b28..c0887ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -24,6 +24,7 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.systemui.DejankUtils;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 
@@ -117,12 +118,12 @@
     public void onAllPanelsCollapsed() {
         super.onAllPanelsCollapsed();
         // Close the status bar in the next frame so we can show the end of the animation.
-        postOnAnimation(mHideExpandedRunnable);
+        DejankUtils.postAfterTraversal(mHideExpandedRunnable);
         mLastFullyOpenedPanel = null;
     }
 
     public void removePendingHideExpandedRunnables() {
-        removeCallbacks(mHideExpandedRunnable);
+        DejankUtils.removeCallbacks(mHideExpandedRunnable);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 1e3bc4d..41aeac9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.policy;
 
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -27,6 +26,8 @@
 
 import com.android.settingslib.TetherUtil;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 public class HotspotControllerImpl implements HotspotController {
@@ -43,11 +44,32 @@
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final Receiver mReceiver = new Receiver();
     private final Context mContext;
-    private final WifiManager mWifiManager;
+
+    private int mHotspotState;
 
     public HotspotControllerImpl(Context context) {
         mContext = context;
-        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("HotspotController state:");
+        pw.print("  mHotspotEnabled="); pw.println(stateToString(mHotspotState));
+    }
+
+    private static String stateToString(int hotspotState) {
+        switch (hotspotState) {
+            case WifiManager.WIFI_AP_STATE_DISABLED:
+                return "DISABLED";
+            case WifiManager.WIFI_AP_STATE_DISABLING:
+                return "DISABLING";
+            case WifiManager.WIFI_AP_STATE_ENABLED:
+                return "ENABLED";
+            case WifiManager.WIFI_AP_STATE_ENABLING:
+                return "ENABLING";
+            case WifiManager.WIFI_AP_STATE_FAILED:
+                return "FAILED";
+        }
+        return null;
     }
 
     public void addCallback(Callback callback) {
@@ -66,7 +88,7 @@
 
     @Override
     public boolean isHotspotEnabled() {
-        return mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED;
+        return mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED;
     }
 
     @Override
@@ -76,7 +98,6 @@
 
     @Override
     public void setHotspotEnabled(boolean enabled) {
-        final ContentResolver cr = mContext.getContentResolver();
         // Call provisioning app which is called when enabling Tethering from Settings
         if (enabled && TetherUtil.isProvisioningNeeded(mContext)) {
             mContext.startServiceAsUser(TETHER_SERVICE_INTENT, UserHandle.CURRENT);
@@ -113,7 +134,8 @@
             if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction());
             int state = intent.getIntExtra(
                     WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
-            fireCallback(WifiManager.WIFI_AP_STATE_ENABLED == state);
+            mHotspotState = state;
+            fireCallback(mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED);
         }
     }
 }
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlend.java b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
index 906e0f6..6b09bb7 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlend.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
@@ -360,6 +360,8 @@
     /**
      * dst = dst.rgb * src.a + (1.0 - dst.a) * src.rgb
      * dst.a = src.a
+     * Note: Before API 23, the alpha channel was not correctly set.
+     *       Please use with caution when targeting older APIs.
      *
      * @param ain The source buffer
      * @param aout The destination buffer
@@ -371,6 +373,8 @@
     /**
      * dst = dst.rgb * src.a + (1.0 - dst.a) * src.rgb
      * dst.a = src.a
+     * Note: Before API 23, the alpha channel was not correctly set.
+     *       Please use with caution when targeting older APIs.
      *
      * @param ain The source buffer
      * @param aout The destination buffer
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a270974..cb294fd 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -534,9 +534,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             return readPasswordInternal(accounts, account);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -572,9 +573,10 @@
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account is null");
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             return readPreviousNameInternal(accounts, account);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -632,9 +634,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             return readUserDataInternal(accounts, account, key);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -709,11 +712,12 @@
          *     a limited user.
          */
 
-        UserAccounts accounts = getUserAccountsForCaller();
         // fails if the account already exists
         int uid = getCallingUid();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             return addAccountInternal(accounts, account, password, extras, false, uid);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -801,6 +805,13 @@
         if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
             return false;
         }
+        int user = UserHandle.getCallingUserId();
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(user);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
         return updateLastAuthenticatedTime(account);
     }
 
@@ -975,9 +986,10 @@
         if (account == null) throw new IllegalArgumentException("account is null");
         if (features == null) throw new IllegalArgumentException("features is null");
         checkReadAccountsPermitted(callingUid, account.type);
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             new TestFeaturesSession(accounts, response, account, features).bind();
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1057,9 +1069,10 @@
                     accountToRename.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
             Bundle result = new Bundle();
             result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
@@ -1206,7 +1219,6 @@
             throw new SecurityException(msg);
         }
 
-        UserAccounts accounts = getUserAccounts(userId);
         if (!canUserModifyAccounts(userId)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
@@ -1227,6 +1239,7 @@
         UserHandle user = new UserHandle(userId);
         long identityToken = clearCallingIdentity();
 
+        UserAccounts accounts = getUserAccounts(userId);
         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
         synchronized(accounts.credentialsPermissionNotificationIds) {
             for (Pair<Pair<Account, String>, Integer> pair:
@@ -1381,9 +1394,10 @@
         }
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         if (authToken == null) throw new IllegalArgumentException("authToken is null");
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             synchronized (accounts.cacheLock) {
                 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
                 db.beginTransaction();
@@ -1517,9 +1531,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             return readAuthTokenInternal(accounts, account, authTokenType);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1544,9 +1559,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1569,9 +1585,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             setPasswordInternal(accounts, account, password, callingUid);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1632,9 +1649,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             setPasswordInternal(accounts, account, null, callingUid);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1659,9 +1677,10 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             setUserdataInternal(accounts, account, key, value);
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1734,9 +1753,10 @@
         if (callingUid != Process.SYSTEM_UID) {
             throw new SecurityException("can only call from system");
         }
-        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
+        int userId = UserHandle.getUserId(callingUid);
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, accountType, false /* expectActivityLaunch */,
                     false /* stripAuthTokenFromResult */,  null /* accountName */,
                     false /* authDetailsRequired */) {
@@ -1803,11 +1823,17 @@
             Slog.w(TAG, "Failed to report error back to the client." + e);
             return;
         }
-
-        final UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
+        long ident = Binder.clearCallingIdentity();
+        final UserAccounts accounts;
         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
-        authenticatorInfo = mAuthenticatorCache.getServiceInfo(
-                AuthenticatorDescription.newKey(account.type), accounts.userId);
+        try {
+            accounts = getUserAccounts(userId);
+            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
+                    AuthenticatorDescription.newKey(account.type), accounts.userId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
 
         final boolean customTokens =
                 authenticatorInfo != null && authenticatorInfo.type.customTokens;
@@ -1820,7 +1846,7 @@
         // Get the calling package. We will use it for the purpose of caching.
         final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
         List<String> callerOwnedPackageNames;
-        long ident = Binder.clearCallingIdentity();
+        ident = Binder.clearCallingIdentity();
         try {
             callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
         } finally {
@@ -2108,17 +2134,18 @@
             return;
         }
 
-        UserAccounts accounts = getUserAccountsForCaller();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
         options.putInt(AccountManager.KEY_CALLER_UID, uid);
         options.putInt(AccountManager.KEY_CALLER_PID, pid);
 
-        logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS);
-
+        int usrId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(usrId);
+            logRecordWithUid(
+                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
             new Session(accounts, response, accountType, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, null /* accountName */,
                     false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
@@ -2190,17 +2217,17 @@
             return;
         }
 
-        UserAccounts accounts = getUserAccounts(userId);
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
         options.putInt(AccountManager.KEY_CALLER_UID, uid);
         options.putInt(AccountManager.KEY_CALLER_PID, pid);
 
-        logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS);
-
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
+            logRecordWithUid(
+                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
             new Session(accounts, response, accountType, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, null /* accountName */,
                     false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
@@ -2262,9 +2289,9 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
-        UserAccounts accounts = getUserAccounts(userId);
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, account.type, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, account.name,
                     true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
@@ -2298,9 +2325,10 @@
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, account.type, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, account.name,
                     false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
@@ -2342,9 +2370,10 @@
                     accountType);
             throw new SecurityException(msg);
         }
-        UserAccounts accounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             new Session(accounts, response, accountType, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, null /* accountName */,
                     false /* authDetailsRequired */) {
@@ -2468,13 +2497,13 @@
      * @hide
      */
     public Account[] getAccounts(int userId) {
-        UserAccounts accounts = getUserAccounts(userId);
         int callingUid = Binder.getCallingUid();
         if (!isReadAccountsPermitted(callingUid, null)) {
             return new Account[0];
         }
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts accounts = getUserAccounts(userId);
             synchronized (accounts.cacheLock) {
                 return getAccountsFromCacheLocked(accounts, null, callingUid, null);
             }
@@ -2720,9 +2749,10 @@
             }
             return;
         }
-        UserAccounts userAccounts = getUserAccountsForCaller();
+        int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
+            UserAccounts userAccounts = getUserAccounts(userId);
             if (features == null || features.length == 0) {
                 Account[] accounts;
                 synchronized (userAccounts.cacheLock) {
@@ -3227,6 +3257,11 @@
         logRecord(db, action, tableName, -1, accounts);
     }
 
+    private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
+        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+        logRecord(db, action, tableName, -1, accounts, uid);
+    }
+
     /*
      * This function receives an opened writable database.
      */
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 9f11def..6ee1650 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -446,13 +446,23 @@
                 if (i > MAX_RECENT_BITMAPS) {
                     tr.freeLastThumbnail();
                 }
-                if (task.realActivity == null || tr.realActivity == null ||
-                        !task.realActivity.equals(tr.realActivity)) {
+                final boolean sameAffinity =
+                        task.affinity != null && task.affinity.equals(tr.affinity);
+                final boolean trIsDocument = tr.intent != null && tr.intent.isDocument();
+                final boolean bothDocuments = document && trIsDocument;
+                if (!sameAffinity && !bothDocuments) {
+                    // Not the same affinity and not documents. Move along...
                     continue;
                 }
-                final boolean trIsDocument = tr.intent != null && tr.intent.isDocument();
-                if (document && trIsDocument) {
-                    // These are the same document activity (not necessarily the same doc).
+
+                if (bothDocuments) {
+                    // Do these documents belong to the same activity?
+                    final boolean sameActivity = task.realActivity != null
+                            && tr.realActivity != null
+                            && task.realActivity.equals(tr.realActivity);
+                    if (!sameActivity) {
+                        continue;
+                    }
                     if (maxRecents > 0) {
                         --maxRecents;
                         continue;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 569a0fc..f92f631 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -241,19 +241,10 @@
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
-            int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
             // Adjust the volume with a handler not to be blocked by other system service.
-            if (useSuggested) {
-                if (AudioSystem.isStreamActive(stream, 0)) {
-                    postAdjustSuggestedStreamVolume(stream, direction, flags, packageName, uid);
-                } else {
-                    flags |= previousFlagPlaySound;
-                    postAdjustSuggestedStreamVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
-                            flags, packageName, uid);
-                }
-            } else {
-                postAdjustStreamVolume(stream, direction, flags, packageName, uid);
-            }
+            int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+            postAdjustLocalVolume(stream, direction, flags, packageName, uid, useSuggested,
+                    previousFlagPlaySound);
         } else {
             if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                 // Nothing to do, the volume cannot be changed
@@ -459,24 +450,25 @@
         return mPackageName + "/" + mTag;
     }
 
-    private void postAdjustSuggestedStreamVolume(final int streamType, final int direction,
-            final int flags, final String callingPackage, final int uid) {
+    private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
+            final String packageName, final int uid, final boolean useSuggested,
+            final int previousFlagPlaySound) {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(streamType, direction,
-                        flags, callingPackage, uid);
-            }
-        });
-    }
-
-    private void postAdjustStreamVolume(final int streamType, final int direction, final int flags,
-            final String callingPackage, final int uid) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mAudioManagerInternal.adjustStreamVolumeForUid(streamType, direction, flags,
-                        callingPackage, uid);
+                if (useSuggested) {
+                    if (AudioSystem.isStreamActive(stream, 0)) {
+                        mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
+                                flags, packageName, uid);
+                    } else {
+                        mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+                                AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
+                                flags | previousFlagPlaySound, packageName, uid);
+                    }
+                } else {
+                    mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+                            packageName, uid);
+                }
             }
         });
     }
@@ -1067,23 +1059,22 @@
 
         @Override
         public ParcelableVolumeInfo getVolumeAttributes() {
+            int volumeType;
+            AudioAttributes attributes;
             synchronized (mLock) {
-                int type;
-                int max;
-                int current;
                 if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
-                    type = mVolumeControlType;
-                    max = mMaxVolume;
-                    current = mOptimisticVolume != -1 ? mOptimisticVolume
-                            : mCurrentVolume;
-                } else {
-                    int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
-                    type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
-                    max = mAudioManager.getStreamMaxVolume(stream);
-                    current = mAudioManager.getStreamVolume(stream);
+                    int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume;
+                    return new ParcelableVolumeInfo(
+                            mVolumeType, mAudioAttrs, mVolumeControlType, mMaxVolume, current);
                 }
-                return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current);
+                volumeType = mVolumeType;
+                attributes = mAudioAttrs;
             }
+            int stream = AudioAttributes.toLegacyStreamType(attributes);
+            int max = mAudioManager.getStreamMaxVolume(stream);
+            int current = mAudioManager.getStreamVolume(stream);
+            return new ParcelableVolumeInfo(
+                    volumeType, attributes, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, current);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c763315..c139389 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12275,6 +12275,20 @@
             // We did an in-place move, so dex is ready to roll
             scanFlags |= SCAN_NO_DEX;
             scanFlags |= SCAN_MOVE;
+
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(pkgName);
+                if (ps == null) {
+                    res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Missing settings for moved package " + pkgName);
+                }
+
+                // We moved the entire application as-is, so bring over the
+                // previously derived ABI information.
+                pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
+                pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
+            }
+
         } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
             // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
             scanFlags |= SCAN_NO_DEX;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 51df31f..a5344b4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -133,7 +133,7 @@
         mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
 
         synchronized (mLock) {
-            mUserStates.put(mCurrentUserId, new UserState(mContext, mCurrentUserId));
+            getOrCreateUserStateLocked(mCurrentUserId);
         }
     }
 
@@ -222,7 +222,7 @@
             @Override
             public void onPackageRemoved(String packageName, int uid) {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(getChangingUserId());
+                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
                     if (!userState.packageSet.contains(packageName)) {
                         // Not a TV input package.
                         return;
@@ -281,7 +281,7 @@
     }
 
     private void buildTvInputListLocked(int userId, String[] updatedPackages) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         userState.packageSet.clear();
 
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
@@ -368,7 +368,7 @@
     }
 
     private void buildTvContentRatingSystemListLocked(int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         userState.contentRatingSystemList.clear();
 
         final PackageManager pm = mContext.getPackageManager();
@@ -402,11 +402,7 @@
             clearSessionAndServiceStatesLocked(mUserStates.get(mCurrentUserId));
 
             mCurrentUserId = userId;
-            UserState userState = mUserStates.get(userId);
-            if (userState == null) {
-                userState = new UserState(mContext, userId);
-                mUserStates.put(userId, userState);
-            }
+            getOrCreateUserStateLocked(userId);
             buildTvInputListLocked(userId, null);
             buildTvContentRatingSystemListLocked(userId);
             mWatchLogHandler.obtainMessage(WatchLogHandler.MSG_SWITCH_CONTENT_RESOLVER,
@@ -473,16 +469,17 @@
         return context.getContentResolver();
     }
 
-    private UserState getUserStateLocked(int userId) {
+    private UserState getOrCreateUserStateLocked(int userId) {
         UserState userState = mUserStates.get(userId);
         if (userState == null) {
-            throw new IllegalStateException("User state not found for user ID " + userId);
+            userState = new UserState(mContext, userId);
+            mUserStates.put(userId, userState);
         }
         return userState;
     }
 
     private ServiceState getServiceStateLocked(ComponentName component, int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         ServiceState serviceState = userState.serviceStateMap.get(component);
         if (serviceState == null) {
             throw new IllegalStateException("Service state not found for " + component + " (userId="
@@ -492,7 +489,7 @@
     }
 
     private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (sessionState == null) {
             throw new SessionNotFoundException("Session state not found for token " + sessionToken);
@@ -530,7 +527,7 @@
     }
 
     private void updateServiceConnectionLocked(ComponentName component, int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         ServiceState serviceState = userState.serviceStateMap.get(component);
         if (serviceState == null) {
             return;
@@ -574,7 +571,7 @@
     private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
             String inputId, int userId) {
         // Let clients know the create session requests are failed.
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         List<SessionState> sessionsToAbort = new ArrayList<>();
         for (IBinder sessionToken : serviceState.sessionTokens) {
             SessionState sessionState = userState.sessionStateMap.get(sessionToken);
@@ -593,7 +590,7 @@
 
     private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
             int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
             Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
@@ -629,7 +626,7 @@
         try {
             sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
             if (sessionState.session != null) {
-                UserState userState = getUserStateLocked(userId);
+                UserState userState = getOrCreateUserStateLocked(userId);
                 if (sessionToken == userState.mainSessionToken) {
                     setMainLocked(sessionToken, false, callingUid, userId);
                 }
@@ -646,7 +643,7 @@
     }
 
     private void removeSessionStateLocked(IBinder sessionToken, int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         if (sessionToken == userState.mainSessionToken) {
             if (DEBUG) {
                 Slog.d(TAG, "mainSessionToken=null");
@@ -768,7 +765,7 @@
     }
 
     private void setStateLocked(String inputId, int state, int userId) {
-        UserState userState = getUserStateLocked(userId);
+        UserState userState = getOrCreateUserStateLocked(userId);
         TvInputState inputState = userState.inputMap.get(inputId);
         ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
         int oldState = inputState.state;
@@ -791,7 +788,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     List<TvInputInfo> inputList = new ArrayList<>();
                     for (TvInputState state : userState.inputMap.values()) {
                         inputList.add(state.info);
@@ -810,7 +807,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     TvInputState state = userState.inputMap.get(inputId);
                     return state == null ? null : state.info;
                 }
@@ -826,7 +823,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     TvInputState state = userState.inputMap.get(inputId);
                     return state == null ? INPUT_STATE_CONNECTED : state.state;
                 }
@@ -842,7 +839,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     return userState.contentRatingSystemList;
                 }
             } finally {
@@ -857,7 +854,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    final UserState userState = getUserStateLocked(resolvedUserId);
+                    final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.callbackSet.add(callback);
                     try {
                         callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
@@ -886,7 +883,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.callbackSet.remove(callback);
                 }
             } finally {
@@ -901,7 +898,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     return userState.persistentDataStore.isParentalControlsEnabled();
                 }
             } finally {
@@ -917,7 +914,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.persistentDataStore.setParentalControlsEnabled(enabled);
                 }
             } finally {
@@ -932,7 +929,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     return userState.persistentDataStore.isRatingBlocked(
                             TvContentRating.unflattenFromString(rating));
                 }
@@ -948,7 +945,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     List<String> ratings = new ArrayList<>();
                     for (TvContentRating rating
                             : userState.persistentDataStore.getBlockedRatings()) {
@@ -969,7 +966,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.persistentDataStore.addBlockedRating(
                             TvContentRating.unflattenFromString(rating));
                 }
@@ -986,7 +983,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.persistentDataStore.removeBlockedRating(
                             TvContentRating.unflattenFromString(rating));
                 }
@@ -1013,7 +1010,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     TvInputState inputState = userState.inputMap.get(inputId);
                     if (inputState == null) {
                         Slog.w(TAG, "Failed to find input state for inputId=" + inputId);
@@ -1084,7 +1081,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     if (userState.mainSessionToken == sessionToken) {
                         return;
                     }
@@ -1211,7 +1208,7 @@
                             return;
                         }
 
-                        UserState userState = getUserStateLocked(resolvedUserId);
+                        UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
 
                         // Log the start of watch.
@@ -1639,7 +1636,7 @@
             try {
                 String hardwareInputId = null;
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     if (userState.inputMap.get(inputId) == null) {
                         Slog.e(TAG, "input not found for " + inputId);
                         return false;
@@ -1669,7 +1666,7 @@
                     userId, "isSingleSessionActive");
             try {
                 synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     if (userState.sessionStateMap.size() == 1) {
                         return true;
                     } else if (userState.sessionStateMap.size() == 2) {
@@ -1710,7 +1707,7 @@
 
                 for (int i = 0; i < mUserStates.size(); i++) {
                     int userId = mUserStates.keyAt(i);
-                    UserState userState = getUserStateLocked(userId);
+                    UserState userState = getOrCreateUserStateLocked(userId);
                     pw.println("UserState (" + userId + "):");
                     pw.increaseIndent();
 
@@ -1864,7 +1861,7 @@
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(userId);
+                UserState userState = getOrCreateUserStateLocked(userId);
                 // DO NOT remove the client state of clientStateMap in this method. It will be
                 // removed in releaseSessionLocked().
                 ClientState clientState = userState.clientStateMap.get(clientToken);
@@ -1945,7 +1942,7 @@
                     }
                 }
                 // If there are any other sessions based on this session, they should be released.
-                UserState userState = getUserStateLocked(userId);
+                UserState userState = getOrCreateUserStateLocked(userId);
                 for (SessionState sessionState : userState.sessionStateMap.values()) {
                     if (sessionToken == sessionState.hardwareSessionToken) {
                         releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
@@ -1977,7 +1974,7 @@
                 Slog.d(TAG, "onServiceConnected(component=" + component + ")");
             }
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mUserId);
+                UserState userState = getOrCreateUserStateLocked(mUserId);
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                 serviceState.service = ITvInputService.Stub.asInterface(service);
 
@@ -2038,7 +2035,7 @@
                         + mComponent + " (expected), " + component + " (actual).");
             }
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mUserId);
+                UserState userState = getOrCreateUserStateLocked(mUserId);
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                 if (serviceState != null) {
                     serviceState.reconnecting = true;
@@ -2163,7 +2160,7 @@
             }
 
             IBinder clientToken = mSessionState.client.asBinder();
-            UserState userState = getUserStateLocked(mSessionState.userId);
+            UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
             ClientState clientState = userState.clientStateMap.get(clientToken);
             if (clientState == null) {
                 clientState = new ClientState(clientToken, mSessionState.userId);
@@ -2509,7 +2506,7 @@
         @Override
         public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mCurrentUserId);
+                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
                     if (!serviceState.isHardware || serviceState.service == null) continue;
@@ -2525,7 +2522,7 @@
         @Override
         public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mCurrentUserId);
+                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
                     if (!serviceState.isHardware || serviceState.service == null) continue;
@@ -2541,7 +2538,7 @@
         @Override
         public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mCurrentUserId);
+                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
                     if (!serviceState.isHardware || serviceState.service == null) continue;
@@ -2557,7 +2554,7 @@
         @Override
         public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mCurrentUserId);
+                UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
                     if (!serviceState.isHardware || serviceState.service == null) continue;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 7630178..85a9624 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -500,7 +500,13 @@
                         mPostKeyguardExitAnimation.getStartOffset(),
                         mPostKeyguardExitAnimation.getDuration());
                 mKeyguardGoingAway = false;
-            } else if (mPostKeyguardExitAnimation.hasEnded()) {
+            }
+            // mPostKeyguardExitAnimation might either be ended normally, cancelled, or "orphaned",
+            // meaning that the window it was running on was removed. We check for hasEnded() for
+            // ended normally and cancelled case, and check the time for the "orphaned" case.
+            else if (mPostKeyguardExitAnimation.hasEnded()
+                    || mCurrentTime - mPostKeyguardExitAnimation.getStartTime()
+                            > mPostKeyguardExitAnimation.getDuration()) {
                 // Done with the animation, reset.
                 if (DEBUG_KEYGUARD) Slog.v(TAG, "Done with Keyguard exit animations.");
                 mPostKeyguardExitAnimation = null;
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 653cbd8..1787b91 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -122,6 +122,8 @@
     // Request is cancelled if host does not configure device within 10 seconds.
     private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000;
 
+    private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
+
     private UsbHandler mHandler;
     private boolean mBootCompleted;
 
@@ -468,6 +470,7 @@
                 functions = getDefaultFunctions();
             }
             functions = applyAdbFunction(functions);
+            functions = applyOemOverrideFunction(functions);
             functions = applyUserRestrictions(functions);
 
             if (!mCurrentFunctions.equals(functions) || !mCurrentFunctionsApplied
@@ -888,6 +891,24 @@
         }
     }
 
+    private String applyOemOverrideFunction(String usbFunctions) {
+        if ((usbFunctions == null) || (mOemModeMap == null)) return usbFunctions;
+
+        String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+
+        List<Pair<String, String>> overrides = mOemModeMap.get(bootMode);
+        if (overrides != null) {
+            for (Pair<String, String> pair: overrides) {
+                if (pair.first.equals(usbFunctions)) {
+                    Slog.d(TAG, "OEM USB override: " + pair.first + " ==> " + pair.second);
+                    return pair.second;
+                }
+            }
+        }
+        // return passed in functions as is.
+        return usbFunctions;
+    }
+
     public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
         if (mDebuggingManager != null) {
             mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey);
diff --git a/tests/AccessibilityEventsLogger/Android.mk b/tests/AccessibilityEventsLogger/Android.mk
new file mode 100644
index 0000000..52bc579
--- /dev/null
+++ b/tests/AccessibilityEventsLogger/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AccessibilityEventsLogger
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
diff --git a/tests/AccessibilityEventsLogger/AndroidManifest.xml b/tests/AccessibilityEventsLogger/AndroidManifest.xml
new file mode 100644
index 0000000..d86769f
--- /dev/null
+++ b/tests/AccessibilityEventsLogger/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.tests.accessibilityeventlogger"
+    android:versionCode="1"
+    android:versionName="0.0" >
+
+    <uses-sdk
+        android:minSdkVersion="18"
+        android:targetSdkVersion="18" />
+
+    <application
+        android:allowBackup="true"
+        android:enabled="true"
+        android:label="@string/app_name" >
+
+        <service
+            android:name=".AELogger"
+            android:enabled="true"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+            android:configChanges="locale" >
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService"/>
+
+                <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC"/>
+                <category android:name="android.accessibilityservice.category.FEEDBACK_VISUAL"/>
+                <category android:name="android.accessibilityservice.category.FEEDBACK_AUDIBLE"/>
+            </intent-filter>
+
+            <meta-data
+                android:name="android.accessibilityservice"
+                android:resource="@xml/accessibilityservice" />
+        </service>
+    </application>
+</manifest>
diff --git a/tests/AccessibilityEventsLogger/res/values/strings.xml b/tests/AccessibilityEventsLogger/res/values/strings.xml
new file mode 100644
index 0000000..353f912
--- /dev/null
+++ b/tests/AccessibilityEventsLogger/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- CHAR LIMIT=none -->
+    <string name="app_name">
+        Accessibility Event Logger
+    </string>
+
+    <!-- CHAR LIMIT=none -->
+    <string name="service_description">
+        Debugging service
+    </string>
+</resources>
diff --git a/tests/AccessibilityEventsLogger/res/xml/accessibilityservice.xml b/tests/AccessibilityEventsLogger/res/xml/accessibilityservice.xml
new file mode 100644
index 0000000..69ecd61
--- /dev/null
+++ b/tests/AccessibilityEventsLogger/res/xml/accessibilityservice.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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.
+-->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accessibilityEventTypes="typeAllMask"
+    android:accessibilityFeedbackType="feedbackGeneric|feedbackVisual|feedbackAudible"
+    android:canRetrieveWindowContent="true"
+    android:accessibilityFlags="flagDefault"
+    android:description="@string/service_description"
+    android:notificationTimeout="0" />
diff --git a/tests/AccessibilityEventsLogger/src/com/android/tests/accessibilityeventslogger/AELogger.java b/tests/AccessibilityEventsLogger/src/com/android/tests/accessibilityeventslogger/AELogger.java
new file mode 100644
index 0000000..27d8eb9
--- /dev/null
+++ b/tests/AccessibilityEventsLogger/src/com/android/tests/accessibilityeventslogger/AELogger.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.tests.accessibilityeventslogger;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.Toast;
+
+import java.util.Locale;
+
+public class AELogger extends AccessibilityService {
+    private static final String TAG = AELogger.class.getCanonicalName();
+
+    private static final int TOAST_EVENT_TYPES =
+            AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
+
+    @Override
+    public void onServiceConnected() {
+      super.onServiceConnected();
+      Log.v(TAG, "Service connected.");
+    }
+
+
+    @Override
+    public void onInterrupt() {
+        // Do nothing
+    }
+
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {
+        final String eventClass = event.getClassName().toString();
+        final String eventText = String.valueOf(event.getText()).toLowerCase(Locale.getDefault());
+        final String eventType = AccessibilityEvent.eventTypeToString(event.getEventType());
+
+        Log.d(TAG, String.format(
+                    "typ=%s cls=%s pkg=%s txt=%s dsc=%s",
+                    eventType,
+                    eventClass,
+                    event.getPackageName(),
+                    eventText,
+                    event.getContentDescription()
+                    ));
+
+        // Show selected event types
+        if (0 != (TOAST_EVENT_TYPES & event.getEventType())) {
+            final Toast toast = Toast.makeText(this,
+                    eventType + ": " + eventClass, Toast.LENGTH_SHORT);
+            toast.show();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 23df3f1..6a9d5dd6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -526,6 +526,11 @@
                         if (neededWidth > measuredWidth) {
                             mMeasuredScreenWidth += neededWidth - measuredWidth;
                         }
+                        if (mMeasuredScreenWidth < measuredWidth) {
+                            // If the screen width is less than the exact measured width,
+                            // expand to match.
+                            mMeasuredScreenWidth = measuredWidth;
+                        }
                     }
 
                     if (renderingMode.isVertExpand()) {
@@ -534,6 +539,11 @@
                         if (neededHeight > measuredHeight) {
                             mMeasuredScreenHeight += neededHeight - measuredHeight;
                         }
+                        if (mMeasuredScreenHeight < measuredHeight) {
+                            // If the screen height is less than the exact measured height,
+                            // expand to match.
+                            mMeasuredScreenHeight = measuredHeight;
+                        }
                     }
                 }
             }
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
index e172b2d..6c351da 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
index d5b81c4..6d7c719 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
new file mode 100644
index 0000000..92eb3e1
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
new file mode 100644
index 0000000..81755ce
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_horz_layout.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_horz_layout.xml
new file mode 100644
index 0000000..2c66b7f
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_horz_layout.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:padding="16dp"
+              android:orientation="horizontal"
+              android:background="#AAAAAA"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content">
+
+    <include layout="@layout/expand_layout"
+             android:layout_height="wrap_content"
+             android:layout_width="wrap_content" />
+
+</LinearLayout>
+
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_layout.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_layout.xml
new file mode 100644
index 0000000..a255da7
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_layout.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <TextView
+        android:background="#FF0000"
+        android:textSize="200sp"
+        android:layout_width="200dp"
+        android:layout_height="200dp" />
+    <TextView
+        android:background="#00FF00"
+        android:textSize="200sp"
+        android:layout_width="200dp"
+        android:layout_height="200dp" />
+    <TextView
+        android:background="#0000FF"
+        android:textSize="200sp"
+        android:layout_width="200dp"
+        android:layout_height="200dp" />
+    <TextView
+        android:background="#FF00FF"
+        android:textSize="200sp"
+        android:layout_width="200dp"
+        android:layout_height="200dp" />
+    <TextView
+        android:background="#00FFFF"
+        android:textSize="200sp"
+        android:layout_width="200dp"
+        android:layout_height="200dp" />
+</merge>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_vert_layout.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_vert_layout.xml
new file mode 100644
index 0000000..5319654
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/expand_vert_layout.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:padding="16dp"
+              android:orientation="vertical"
+              android:background="#AAAAAA"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content">
+
+    <include layout="@layout/expand_layout"
+             android:layout_height="wrap_content"
+             android:layout_width="wrap_content" />
+
+</LinearLayout>
+
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 272a2b8..b2909c9 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -31,6 +31,8 @@
 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.resources.Density;
+import com.android.resources.Navigation;
 import com.android.utils.ILogger;
 
 import org.junit.AfterClass;
@@ -310,21 +312,52 @@
         sBridge = null;
     }
 
+    /** Test expand_layout.xml */
+    @Test
+    public void testExpand() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
+                "expand_vert_layout.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
+        layoutLibCallback.initResources();
+
+        ConfigGenerator customConfigGenerator = new ConfigGenerator()
+                .setScreenWidth(300)
+                .setScreenHeight(20)
+                .setDensity(Density.XHIGH)
+                .setNavigation(Navigation.NONAV);
+
+        SessionParams params = getSessionParams(parser, customConfigGenerator,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", RenderingMode.V_SCROLL,
+                22);
+
+        renderAndVerify(params, "expand_vert_layout.png");
+
+        customConfigGenerator = new ConfigGenerator()
+                .setScreenWidth(20)
+                .setScreenHeight(300)
+                .setDensity(Density.XHIGH)
+                .setNavigation(Navigation.NONAV);
+        parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
+                "expand_horz_layout.xml");
+        params = getSessionParams(parser, customConfigGenerator,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", RenderingMode
+                        .H_SCROLL, 22);
+
+        renderAndVerify(params, "expand_horz_layout.png");
+    }
+
     /**
      * Create a new rendering session and test that rendering given layout on nexus 5
      * doesn't throw any exceptions and matches the provided image.
      */
-    private void renderAndVerify(String layoutFileName, String goldenFileName)
+    private void renderAndVerify(SessionParams params, String goldenFileName)
             throws ClassNotFoundException {
-        // Create the layout pull parser.
-        LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
-        // Create LayoutLibCallback.
-        LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
-        layoutLibCallback.initResources();
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, layoutLibCallback);
         RenderSession session = sBridge.createSession(params);
+
         if (!session.getResult().isSuccess()) {
             getLogger().error(session.getResult().getException(),
                     session.getResult().getErrorMessage());
@@ -344,25 +377,44 @@
     }
 
     /**
+     * Create a new rendering session and test that rendering given layout on nexus 5
+     * doesn't throw any exceptions and matches the provided image.
+     */
+    private void renderAndVerify(String layoutFileName, String goldenFileName)
+            throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
+        layoutLibCallback.initResources();
+        // TODO: Set up action bar handler properly to test menu rendering.
+        // Create session params.
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.Light.DarkActionBar", RenderingMode.NORMAL, 22);
+        renderAndVerify(params, goldenFileName);
+    }
+
+    /**
      * Uses Theme.Material and Target sdk version as 22.
      */
     private SessionParams getSessionParams(LayoutPullParser layoutParser,
-            ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback) {
+            ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback,
+            String themeName, RenderingMode renderingMode, int targetSdk) {
         FolderConfiguration config = configGenerator.getFolderConfig();
         ResourceResolver resourceResolver =
                 ResourceResolver.create(sProjectResources.getConfiguredResources(config),
                         sFrameworkRepo.getConfiguredResources(config),
-                        "AppTheme", true);
+                        themeName, true);
 
         return new SessionParams(
                 layoutParser,
-                RenderingMode.NORMAL,
+                renderingMode,
                 null /*used for caching*/,
                 configGenerator.getHardwareConfig(),
                 resourceResolver,
                 layoutLibCallback,
                 0,
-                22, // TODO: Make it more configurable to run tests for various versions.
+                targetSdk,
                 getLayoutLog());
     }