Merge "Move 'un/install' to cmd"
diff --git a/api/current.txt b/api/current.txt
index 84e9079..c161242 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15511,6 +15511,7 @@
     field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
     field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
     field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
+    field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
     field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
     field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
     field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
@@ -15562,6 +15563,7 @@
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
     method public void set(int, int[], int[], byte[], byte[], int);
+    method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
     field public byte[] iv;
     field public byte[] key;
     field public int mode;
@@ -15570,6 +15572,13 @@
     field public int numSubSamples;
   }
 
+  public static final class MediaCodec.CryptoInfo.Pattern {
+    ctor public MediaCodec.CryptoInfo.Pattern(int, int);
+    method public int getEncryptBlocks();
+    method public int getSkipBlocks();
+    method public void set(int, int);
+  }
+
   public static abstract interface MediaCodec.OnFrameRenderedListener {
     method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 31634bf..dd6a2839 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16796,6 +16796,7 @@
     field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
     field public static final deprecated int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
     field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
+    field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
     field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
     field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
     field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
@@ -16847,6 +16848,7 @@
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
     method public void set(int, int[], int[], byte[], byte[], int);
+    method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
     field public byte[] iv;
     field public byte[] key;
     field public int mode;
@@ -16855,6 +16857,13 @@
     field public int numSubSamples;
   }
 
+  public static final class MediaCodec.CryptoInfo.Pattern {
+    ctor public MediaCodec.CryptoInfo.Pattern(int, int);
+    method public int getEncryptBlocks();
+    method public int getSkipBlocks();
+    method public void set(int, int);
+  }
+
   public static abstract interface MediaCodec.OnFrameRenderedListener {
     method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
   }
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 6f65889..7f9a5d3 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -955,7 +955,6 @@
         mStarted = true;
         mPaused = false;
         mRunning = false;
-        mAnimationEndRequested = false;
         AnimationHandler animationHandler = AnimationHandler.getInstance();
         animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
 
@@ -1123,6 +1122,8 @@
             Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                     System.identityHashCode(this));
         }
+
+        mAnimationEndRequested = false;
         initAnimation();
         mRunning = true;
         if (mSeekFraction >= 0) {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 477b62c..53b773a 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1505,21 +1505,27 @@
      *
      * @hide
      */
-    public static String localeToResourceQualifier(Locale loc) {
+    public static String localesToResourceQualifier(LocaleList locs) {
         StringBuilder sb = new StringBuilder();
-        boolean l = (loc.getLanguage().length() != 0);
-        boolean c = (loc.getCountry().length() != 0);
-        boolean s = (loc.getScript().length() != 0);
-        boolean v = (loc.getVariant().length() != 0);
-        // TODO: take script and extensions into account
-        if (l) {
-            sb.append(loc.getLanguage());
-            if (c) {
-                sb.append("-r").append(loc.getCountry());
-                if (s) {
-                    sb.append("-s").append(loc.getScript());
-                    if (v) {
-                        sb.append("-v").append(loc.getVariant());
+        for (int i = 0; i < locs.size(); i++) {
+            Locale loc = locs.get(i);
+            boolean l = (loc.getLanguage().length() != 0);
+            boolean c = (loc.getCountry().length() != 0);
+            boolean s = (loc.getScript().length() != 0);
+            boolean v = (loc.getVariant().length() != 0);
+            // TODO: take script and extensions into account
+            if (l) {
+                if (sb.length() != 0) {
+                    sb.append(",");
+                }
+                sb.append(loc.getLanguage());
+                if (c) {
+                    sb.append("-r").append(loc.getCountry());
+                    if (s) {
+                        sb.append("-s").append(loc.getScript());
+                        if (v) {
+                            sb.append("-v").append(loc.getVariant());
+                        }
                     }
                 }
             }
@@ -1544,9 +1550,8 @@
             }
         }
 
-        // TODO: send the whole locale list
-        if (config.locale != null && !config.locale.getLanguage().isEmpty()) {
-            parts.add(localeToResourceQualifier(config.locale));
+        if (!config.mLocaleList.isEmpty()) {
+            parts.add(localesToResourceQualifier(config.mLocaleList));
         }
 
         switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) {
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 7164747..998c72a 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -811,6 +811,65 @@
     }
 
     @SmallTest
+    public void testEndBeforeStart() throws Throwable {
+        // This test calls two animators that are not yet started. One animator has completed a
+        // previous run but hasn't started since then, the other one has never run. When end() is
+        // called on these two animators, we expected their animation listeners to receive both
+        // onAnimationStarted(Animator) and onAnimationEnded(Animator) callbacks, in that sequence.
+
+        a1.setStartDelay(20);
+
+        // First start a1's first run.
+        final MyListener normalEndingListener = new MyListener();
+        a1.addListener(normalEndingListener);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertFalse(a1.isStarted());
+                assertFalse(normalEndingListener.startCalled);
+                assertFalse(normalEndingListener.endCalled);
+                // Start normally
+                a1.start();
+            }
+        });
+
+        Thread.sleep(a1.getTotalDuration() + POLL_INTERVAL);
+
+        // a1 should have finished by now.
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // Call end() on both a1 and a2 without calling start()
+                final MyListener l1 = new MyListener();
+                a1.addListener(l1);
+                final MyListener l2 = new MyListener();
+                a2.addListener(l2);
+
+                assertFalse(a1.isStarted());
+                assertFalse(l1.startCalled);
+                assertFalse(l1.endCalled);
+                assertFalse(a2.isStarted());
+                assertFalse(l2.startCalled);
+                assertFalse(l1.endCalled);
+
+                a1.end();
+                a2.end();
+
+                // Check that both animators' listeners have received the animation callbacks.
+                assertTrue(l1.startCalled);
+                assertTrue(l1.endCalled);
+                assertFalse(a1.isStarted());
+                assertTrue(l1.endTime >= l1.startTime);
+
+                assertTrue(l2.startCalled);
+                assertTrue(l2.endCalled);
+                assertFalse(a2.isStarted());
+                assertTrue(l2.endTime >= l1.startTime);
+            }
+        });
+    }
+
+    @SmallTest
     public void testZeroDuration() throws Throwable {
         // Run two animators with zero duration, with one running forward and the other one
         // backward. Check that the animations start and finish with the correct end fractions.
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 6c224e5..478fd99 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2179,6 +2179,7 @@
     // in media/hardware/CryptoAPI.h !
     public static final int CRYPTO_MODE_UNENCRYPTED = 0;
     public static final int CRYPTO_MODE_AES_CTR     = 1;
+    public static final int CRYPTO_MODE_AES_CBC     = 2;
 
     /**
      * Metadata describing the structure of a (at least partially) encrypted
@@ -2186,27 +2187,14 @@
      * A buffer's data is considered to be partitioned into "subSamples",
      * each subSample starts with a (potentially empty) run of plain,
      * unencrypted bytes followed by a (also potentially empty) run of
-     * encrypted bytes.
-     * numBytesOfClearData can be null to indicate that all data is encrypted.
-     * This information encapsulates per-sample metadata as outlined in
-     * ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base media file format files".
+     * encrypted bytes. If pattern encryption applies, each of the latter runs
+     * is encrypted only partly, according to a repeating pattern of "encrypt"
+     * and "skip" blocks. numBytesOfClearData can be null to indicate that all
+     * data is encrypted. This information encapsulates per-sample metadata as
+     * outlined in ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base
+     * media file format files".
      */
     public final static class CryptoInfo {
-        public void set(
-                int newNumSubSamples,
-                @NonNull int[] newNumBytesOfClearData,
-                @NonNull int[] newNumBytesOfEncryptedData,
-                @NonNull byte[] newKey,
-                @NonNull byte[] newIV,
-                int newMode) {
-            numSubSamples = newNumSubSamples;
-            numBytesOfClearData = newNumBytesOfClearData;
-            numBytesOfEncryptedData = newNumBytesOfEncryptedData;
-            key = newKey;
-            iv = newIV;
-            mode = newMode;
-        }
-
         /**
          * The number of subSamples that make up the buffer's contents.
          */
@@ -2220,7 +2208,7 @@
          */
         public int[] numBytesOfEncryptedData;
         /**
-         * A 16-byte opaque key
+         * A 16-byte key id
          */
         public byte[] key;
         /**
@@ -2229,10 +2217,84 @@
         public byte[] iv;
         /**
          * The type of encryption that has been applied,
-         * see {@link #CRYPTO_MODE_UNENCRYPTED} and {@link #CRYPTO_MODE_AES_CTR}.
+         * see {@link #CRYPTO_MODE_UNENCRYPTED}, {@link #CRYPTO_MODE_AES_CTR}
+         * and {@link #CRYPTO_MODE_AES_CBC}
          */
         public int mode;
 
+        /**
+         * Metadata describing encryption pattern for the protected bytes in a subsample.
+         */
+        public final static class Pattern {
+            /**
+             * Number of blocks to be encrypted in the pattern. If zero, pattern
+             * encryption is inoperative.
+             */
+            private int mEncryptBlocks;
+
+            /**
+             * Number of blocks to be skipped (left clear) in the pattern. If zero,
+             * pattern encryption is inoperative.
+             */
+            private int mSkipBlocks;
+
+            /**
+             * Construct a sample encryption pattern given the number of blocks to
+             * encrypt and skip in the pattern.
+             */
+            public Pattern(int blocksToEncrypt, int blocksToSkip) {
+                set(blocksToEncrypt, blocksToSkip);
+            }
+
+            /**
+             * Set the number of blocks to encrypt and skip in a sample encryption
+             * pattern.
+             */
+            public void set(int blocksToEncrypt, int blocksToSkip) {
+                mEncryptBlocks = blocksToEncrypt;
+                mSkipBlocks = blocksToSkip;
+            }
+
+            /**
+             * Return the number of blocks to skip in a sample encryption pattern.
+             */
+            public int getSkipBlocks() {
+                return mSkipBlocks;
+            }
+
+            /**
+             * Return the number of blocks to encrypt in a sample encryption pattern.
+             */
+            public int getEncryptBlocks() {
+                return mEncryptBlocks;
+            }
+        };
+
+        /**
+         * The pattern applicable to the protected data in each subsample.
+         */
+        private Pattern pattern;
+
+        public void set(
+                int newNumSubSamples,
+                @NonNull int[] newNumBytesOfClearData,
+                @NonNull int[] newNumBytesOfEncryptedData,
+                @NonNull byte[] newKey,
+                @NonNull byte[] newIV,
+                int newMode) {
+            numSubSamples = newNumSubSamples;
+            numBytesOfClearData = newNumBytesOfClearData;
+            numBytesOfEncryptedData = newNumBytesOfEncryptedData;
+            key = newKey;
+            iv = newIV;
+            mode = newMode;
+            pattern = new Pattern(0, 0);
+        }
+
+        public void setPattern(Pattern newPattern) {
+            pattern = newPattern;
+        }
+
         @Override
         public String toString() {
             StringBuilder builder = new StringBuilder();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9ea6722..f36d640 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -20,6 +20,7 @@
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -41,6 +42,7 @@
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
 import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.sax.Element;
 import android.sax.ElementListener;
 import android.sax.RootElement;
@@ -324,8 +326,6 @@
     // used when scanning the image database so we know whether we have to prune
     // old thumbnail files
     private int mOriginalCount;
-    /** Whether the database had any entries in it before the scan started */
-    private boolean mWasEmptyPriorToScan = false;
     /** Whether the scanner has set a default sound for the ringer ringtone. */
     private boolean mDefaultRingtoneSet;
     /** Whether the scanner has set a default sound for the notification ringtone. */
@@ -535,6 +535,18 @@
                 if (mMtpObjectHandle != 0) {
                     entry.mRowId = 0;
                 }
+
+                if ((!mDefaultNotificationSet &&
+                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
+                        || (!mDefaultRingtoneSet &&
+                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
+                        || (!mDefaultAlarmSet &&
+                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename))) {
+                    Log.w(TAG, "forcing rescan of " + entry.mPath +
+                            "since ringtone setting didn't finish");
+                    scanAlways = true;
+                }
+
                 // rescan for metadata if file was modified since last scan
                 if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
                     if (noMedia) {
@@ -914,6 +926,26 @@
             }
             Uri result = null;
             boolean needToSetSettings = false;
+            // Setting a flag in order not to use bulk insert for the file related with
+            // notifications, ringtones, and alarms, because the rowId of the inserted file is
+            // needed.
+            if (notifications && !mDefaultNotificationSet) {
+                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+                    needToSetSettings = true;
+                }
+            } else if (ringtones && !mDefaultRingtoneSet) {
+                if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
+                    needToSetSettings = true;
+                }
+            } else if (alarms && !mDefaultAlarmSet) {
+                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+                    needToSetSettings = true;
+                }
+            }
+
             if (rowId == 0) {
                 if (mMtpObjectHandle != 0) {
                     values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -925,28 +957,6 @@
                     }
                     values.put(Files.FileColumns.FORMAT, format);
                 }
-                // Setting a flag in order not to use bulk insert for the file related with
-                // notifications, ringtones, and alarms, because the rowId of the inserted file is
-                // needed.
-                if (mWasEmptyPriorToScan) {
-                    if (notifications && !mDefaultNotificationSet) {
-                        if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
-                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
-                            needToSetSettings = true;
-                        }
-                    } else if (ringtones && !mDefaultRingtoneSet) {
-                        if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
-                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
-                            needToSetSettings = true;
-                        }
-                    } else if (alarms && !mDefaultAlarmSet) {
-                        if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
-                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
-                            needToSetSettings = true;
-                        }
-                    }
-                }
-
                 // New file, insert it.
                 // Directories need to be inserted before the files they contain, so they
                 // get priority when bulk inserting.
@@ -1016,13 +1026,20 @@
 
         private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
 
+            if(wasSettingAlreadySet(settingName)) {
+                return;
+            }
+
             String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
                     settingName);
 
             if (TextUtils.isEmpty(existingSettingValue)) {
                 // Set the setting to the given URI
-                Settings.System.putString(mContext.getContentResolver(), settingName,
+
+                ContentResolver cr = mContext.getContentResolver();
+                Settings.System.putString(cr, settingName,
                         ContentUris.withAppendedId(uri, rowId).toString());
+                Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
             }
         }
 
@@ -1050,6 +1067,20 @@
 
     }; // end of anonymous MediaScannerClient instance
 
+    private String settingSetIndicatorName(String base) {
+        return base + "_set";
+    }
+
+    private boolean wasSettingAlreadySet(String name) {
+        ContentResolver cr = mContext.getContentResolver();
+        String indicatorName = settingSetIndicatorName(name);
+        try {
+            return Settings.System.getInt(cr, indicatorName) != 0;
+        } catch (SettingNotFoundException e) {
+            return false;
+        }
+    }
+
     private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
         Cursor c = null;
         String where = null;
@@ -1071,6 +1102,10 @@
             selectionArgs = new String[] { "" };
         }
 
+        mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
+        mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
+        mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);
+
         // Tell the provider to not delete the file.
         // If the file is truly gone the delete is unnecessary, and we want to avoid
         // accidentally deleting files that are really there (this may happen if the
@@ -1089,7 +1124,6 @@
                 // with CursorWindow positioning.
                 long lastId = Long.MIN_VALUE;
                 Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
-                mWasEmptyPriorToScan = true;
 
                 while (true) {
                     selectionArgs[0] = "" + lastId;
@@ -1108,7 +1142,6 @@
                     if (num == 0) {
                         break;
                     }
-                    mWasEmptyPriorToScan = false;
                     while (c.moveToNext()) {
                         long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                         String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1260,7 +1293,7 @@
         }
     }
 
-    private void postscan(String[] directories) throws RemoteException {
+    private void postscan(final String[] directories) throws RemoteException {
 
         // handle playlists last, after we know what media files are on the storage.
         if (mProcessPlaylists) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 09efefa..d7664ab 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3058,22 +3058,23 @@
      * purposefully done before acquiring {@link #mPackages} lock.
      */
     private int augmentFlagsForUser(int flags, int userId) {
-        final IActivityManager am = ActivityManagerNative.getDefault();
-        if (am == null) {
-            // We must be early in boot, so the best we can do is assume the
-            // user is fully running.
-            return flags;
-        }
-        final long token = Binder.clearCallingIdentity();
-        try {
-            if (am.isUserRunning(userId, ActivityManager.FLAG_WITH_AMNESIA)) {
-                flags |= PackageManager.FLAG_USER_RUNNING_WITH_AMNESIA;
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        // TODO: bring back once locking fixed
+//        final IActivityManager am = ActivityManagerNative.getDefault();
+//        if (am == null) {
+//            // We must be early in boot, so the best we can do is assume the
+//            // user is fully running.
+//            return flags;
+//        }
+//        final long token = Binder.clearCallingIdentity();
+//        try {
+//            if (am.isUserRunning(userId, ActivityManager.FLAG_WITH_AMNESIA)) {
+//                flags |= PackageManager.FLAG_USER_RUNNING_WITH_AMNESIA;
+//            }
+//        } catch (RemoteException e) {
+//            throw e.rethrowAsRuntimeException();
+//        } finally {
+//            Binder.restoreCallingIdentity(token);
+//        }
         return flags;
     }
 
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 68034c8..25ef8e6 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -295,6 +295,9 @@
 
     Bitmap getAppTransitionThumbnailHeader(int taskId) {
         AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+        if (spec == null) {
+            spec = mDefaultNextAppTransitionAnimationSpec;
+        }
         return spec != null ? spec.bitmap : null;
     }
 
@@ -545,6 +548,9 @@
 
     void getNextAppTransitionStartRect(int taskId, Rect rect) {
         AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+        if (spec == null) {
+            spec = mDefaultNextAppTransitionAnimationSpec;
+        }
         if (spec == null || spec.rect == null) {
             Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
                     new Throwable());
@@ -554,9 +560,10 @@
         }
     }
 
-    private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height) {
+    private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height,
+            Bitmap bitmap) {
         mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
-                null /* bitmap */, new Rect(left, top, left + width, top + height));
+                bitmap, new Rect(left, top, left + width, top + height));
     }
 
     private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
@@ -1346,7 +1353,7 @@
             mNextAppTransitionPackage = null;
             mNextAppTransitionAnimationsSpecs.clear();
             putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
-                    startY + startHeight);
+                    startY + startHeight, null);
             postAnimationCallback();
             mNextAppTransitionCallback = null;
             mAnimationFinishedCallback = null;
@@ -1357,7 +1364,7 @@
                                                 int startWidth, int startHeight) {
         if (isTransitionSet()) {
             mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
-            putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight);
+            putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
             postAnimationCallback();
             mNextAppTransitionCallback = null;
             mAnimationFinishedCallback = null;
@@ -1372,7 +1379,7 @@
             mNextAppTransitionPackage = null;
             mNextAppTransitionAnimationsSpecs.clear();
             mNextAppTransitionScaleUp = scaleUp;
-            putDefaultNextAppTransitionCoordinates(startX, startY, 0 ,0);
+            putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
             postAnimationCallback();
             mNextAppTransitionCallback = startedCallback;
             mAnimationFinishedCallback = null;
@@ -1389,7 +1396,8 @@
             mNextAppTransitionPackage = null;
             mNextAppTransitionAnimationsSpecs.clear();
             mNextAppTransitionScaleUp = scaleUp;
-            putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight);
+            putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight,
+                    srcThumb);
             postAnimationCallback();
             mNextAppTransitionCallback = startedCallback;
             mAnimationFinishedCallback = null;
@@ -1417,7 +1425,7 @@
                         // be set.
                         Rect rect = spec.rect;
                         putDefaultNextAppTransitionCoordinates(rect.left, rect.top, rect.width(),
-                                rect.height());
+                                rect.height(), null);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 87ad1f88..05e88ca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8726,9 +8726,11 @@
                 // we need to go through the process of getting informed by the
                 // application when it has finished drawing.
                 if (w.mOrientationChanging || dragResizingChanged) {
-                    if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Orientation start waiting for draw mDrawState=DRAW_PENDING in "
-                            + w + ", surfaceController " + winAnimator.mSurfaceController);
+                    if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
+                        Slog.v(TAG, "Orientation or resize start waiting for draw"
+                                + ", mDrawState=DRAW_PENDING in " + w
+                                + ", surfaceController " + winAnimator.mSurfaceController);
+                    }
                     winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
                     if (w.mAppToken != null) {
                         w.mAppToken.allDrawn = false;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 81d0b4d..10f737f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -475,7 +475,9 @@
         if (!mLastHidden) {
             //dump();
             mLastHidden = true;
-            mSurfaceController.hideInTransaction(reason);
+            if (mSurfaceController != null) {
+                mSurfaceController.hideInTransaction(reason);
+            }
         }
     }
 
@@ -537,7 +539,9 @@
             return;
         }
         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", null);
-        mSurfaceController.setLayer(mAnimLayer + 1);
+        if (mSurfaceController != null) {
+            mSurfaceController.setLayer(mAnimLayer + 1);
+        }
         mDestroyPreservedSurfaceUponRedraw = true;
         mSurfaceDestroyDeferred = true;
         destroySurfaceLocked();
@@ -752,7 +756,11 @@
                     WindowManagerService.logSurface(mWin, "DESTROY", null);
                     destroySurface();
                 }
-                mWallpaperControllerLocked.hideWallpapers(mWin);
+                // Don't hide wallpaper if we're deferring the surface destroy
+                // because of a surface change.
+                if (!(mSurfaceDestroyDeferred && mDestroyPreservedSurfaceUponRedraw)) {
+                    mWallpaperControllerLocked.hideWallpapers(mWin);
+                }
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Exception thrown when destroying Window " + this
                     + " surface " + mSurfaceController + " session " + mSession
@@ -783,7 +791,11 @@
                     WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
                 }
                 mPendingDestroySurface.destroyInTransaction();
-                mWallpaperControllerLocked.hideWallpapers(mWin);
+                // Don't hide wallpaper if we're destroying a deferred surface
+                // after a surface mode change.
+                if (!mDestroyPreservedSurfaceUponRedraw) {
+                    mWallpaperControllerLocked.hideWallpapers(mWin);
+                }
             }
         } catch (RuntimeException e) {
             Slog.w(TAG, "Exception thrown when destroying Window "
@@ -1328,6 +1340,10 @@
     }
 
     void setTransparentRegionHintLocked(final Region region) {
+        if (mSurfaceController == null) {
+            Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
+            return;
+        }
         mSurfaceController.setTransparentRegionHint(region);
     }
 
@@ -1374,10 +1390,16 @@
     }
 
     void setOpaqueLocked(boolean isOpaque) {
+        if (mSurfaceController == null) {
+            return;
+        }
         mSurfaceController.setOpaque(isOpaque);
     }
 
     void setSecureLocked(boolean isSecure) {
+        if (mSurfaceController == null) {
+            return;
+        }
         mSurfaceController.setSecure(isSecure);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 92ea66b..4b9a538 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1474,6 +1474,7 @@
         final int taskId = appToken.mTask.mTaskId;
         Bitmap thumbnailHeader = mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
         if (thumbnailHeader == null || thumbnailHeader.getConfig() == Bitmap.Config.ALPHA_8) {
+            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
             return;
         }
         // This thumbnail animation is very special, we need to have
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6b1b6296..3b281e2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4848,4 +4848,21 @@
         }
         return null;
     }
-}
+
+    /**
+     * Returns the service state information on specified subscription. Callers require
+     * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+     * @hide
+     */
+    public ServiceState getServiceStateForSubscriber(int subId) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getServiceStateForSubscriber(subId, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index dcece26..8172e94 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -21,9 +21,10 @@
 import android.telecom.PhoneAccount;
 import android.telephony.CellInfo;
 import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.RadioAccessFamily;
-import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
 import java.util.List;
@@ -1005,4 +1006,12 @@
      * Return the modem activity info.
      */
     ModemActivityInfo getModemActivityInfo();
+
+    /**
+     * Get the service state on specified subscription
+     * @param subId Subscription id
+     * @param callingPackage The package making the call
+     * @return Service state on specified subscription.
+     */
+    ServiceState getServiceStateForSubscriber(int subId, String callingPackage);
 }