Merge "Fix contrast enforcement for RemoteInputView" into nyc-dev
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index 3ae9e12..fae0400 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -53,7 +53,6 @@
     libutils \
     liblog \
     libbinder \
-    libnativeloader \
     libandroid_runtime \
     $(app_process_common_shared_libs) \
 
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 7590325..2e02382 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -21,7 +21,6 @@
 #include <cutils/properties.h>
 #include <cutils/trace.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <nativeloader/native_loader.h>
 #include <private/android_filesystem_config.h>  // for AID_SYSTEM
 
 namespace android {
@@ -305,7 +304,6 @@
     }
 
     if (zygote) {
-        InitializeNativeLoader();
         runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
     } else if (className) {
         runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 4db4567..aeda7a2 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1748,7 +1748,7 @@
      *
      * @hide
      */
-    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
+    public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
             @UserIdInt int userHandle) {
         try {
             getContentService().notifyChange(
@@ -1765,7 +1765,7 @@
      *
      * @hide
      */
-    public void notifyChange(Uri uri, ContentObserver observer, @NotifyFlags int flags,
+    public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
             @UserIdInt int userHandle) {
         try {
             getContentService().notifyChange(
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0c79312..03f83d6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -222,7 +222,7 @@
             in String installerPackageName,
             int userId);
 
-    void finishPackageInstall(int token);
+    void finishPackageInstall(int token, boolean didLaunch);
 
     void setInstallerPackageName(in String targetPackage, in String installerPackageName);
 
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 9793cdb..f4237d2 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -13,28 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #define LOG_TAG "SensorManager"
 
-#include <map>
+#include "JNIHelp.h"
+#include "android_os_MessageQueue.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
 
 #include <ScopedUtfChars.h>
 #include <ScopedLocalRef.h>
-
+#include <android_runtime/AndroidRuntime.h>
+#include <gui/Sensor.h>
+#include <gui/SensorEventQueue.h>
+#include <gui/SensorManager.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
 #include <utils/Vector.h>
 
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_os_MessageQueue.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "core_jni_helpers.h"
+#include <endian.h>   // htobe64
+#include <map>
 
 namespace {
 
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index feb8052..520ebe5 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -129,6 +129,10 @@
      * <strong>Note:</strong> This class takes ownership of the passed in file descriptor
      * and is responsible for closing it when the renderer is closed.
      * </p>
+     * <p>
+     * If the file is from an untrusted source it is recommended to run the renderer in a separate,
+     * isolated process with minimal permissions to limit the impact of security exploits.
+     * </p>
      *
      * @param input Seekable file descriptor to read from.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index a026a96..5e9a8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -56,15 +56,6 @@
     private final Rect mClipBounds = new Rect();
     private final int mMinContractedHeight;
     private final int mNotificationContentMarginEnd;
-    private final OnLayoutChangeListener mLayoutUpdater = new OnLayoutChangeListener() {
-        @Override
-        public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                int oldLeft,
-                int oldTop, int oldRight, int oldBottom) {
-            selectLayout(false /* animate */, false /* force */);
-        }
-    };
-
 
     private View mContractedChild;
     private View mExpandedChild;
@@ -119,6 +110,7 @@
     private int mTransformationStartVisibleType;
     private boolean mUserExpanding;
     private int mSingleLineWidthIndention;
+    private boolean mForceSelectNextLayout = true;
     private PendingIntent mPreviousExpandedRemoteInputIntent;
     private PendingIntent mPreviousHeadsUpRemoteInputIntent;
 
@@ -270,6 +262,8 @@
         super.onLayout(changed, left, top, right, bottom);
         updateClipping();
         invalidateOutline();
+        selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
+        mForceSelectNextLayout = false;
     }
 
     @Override
@@ -317,44 +311,35 @@
     public void setContractedChild(View child) {
         if (mContractedChild != null) {
             mContractedChild.animate().cancel();
-            mContractedChild.removeOnLayoutChangeListener(mLayoutUpdater);
             removeView(mContractedChild);
         }
         addView(child);
         mContractedChild = child;
-        mContractedChild.addOnLayoutChangeListener(mLayoutUpdater);
         mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
                 mContainingNotification);
-        selectLayout(false /* animate */, true /* force */);
         mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
     }
 
     public void setExpandedChild(View child) {
         if (mExpandedChild != null) {
             mExpandedChild.animate().cancel();
-            mExpandedChild.removeOnLayoutChangeListener(mLayoutUpdater);
             removeView(mExpandedChild);
         }
         addView(child);
         mExpandedChild = child;
-        mExpandedChild.addOnLayoutChangeListener(mLayoutUpdater);
         mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
                 mContainingNotification);
-        selectLayout(false /* animate */, true /* force */);
     }
 
     public void setHeadsUpChild(View child) {
         if (mHeadsUpChild != null) {
             mHeadsUpChild.animate().cancel();
-            mHeadsUpChild.removeOnLayoutChangeListener(mLayoutUpdater);
             removeView(mHeadsUpChild);
         }
         addView(child);
         mHeadsUpChild = child;
-        mHeadsUpChild.addOnLayoutChangeListener(mLayoutUpdater);
         mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
                 mContainingNotification);
-        selectLayout(false /* animate */, true /* force */);
     }
 
     @Override
@@ -408,7 +393,8 @@
             updateBackgroundColor(true /* animate */);
         }
         if (mTransformationStartVisibleType != UNDEFINED
-                && mVisibleType != mTransformationStartVisibleType) {
+                && mVisibleType != mTransformationStartVisibleType
+                && getViewForVisibleType(mTransformationStartVisibleType) != null) {
             final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
             final TransformableView hiddenView = getTransformableViewForVisibleType(
                     mTransformationStartVisibleType);
@@ -501,26 +487,66 @@
         }
         if (mUserExpanding) {
             updateContentTransformation();
-            return;
-        }
-        int visibleType = calculateVisibleType();
-        if (visibleType != mVisibleType || force) {
+        } else {
+            int visibleType = calculateVisibleType();
+            if (visibleType != mVisibleType || force) {
             View visibleView = getViewForVisibleType(visibleType);
             if (visibleView != null) {
                 visibleView.setVisibility(VISIBLE);
                 transferRemoteInputFocus(visibleType);
             }
 
-            if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
-                    || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
-                    || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
-                    || visibleType == VISIBLE_TYPE_CONTRACTED)) {
-                animateToVisibleType(visibleType);
-            } else {
-                updateViewVisibilities(visibleType);
+                if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
+                        || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
+                        || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
+                        || visibleType == VISIBLE_TYPE_CONTRACTED)) {
+                    animateToVisibleType(visibleType);
+                } else {
+                    updateViewVisibilities(visibleType);
+                }
+                mVisibleType = visibleType;
+                updateBackgroundColor(animate);
             }
-            mVisibleType = visibleType;
-            updateBackgroundColor(animate);
+        }
+        if (mForceSelectNextLayout) {
+            forceUpdateVisibilities();
+        }
+    }
+
+    private void forceUpdateVisibilities() {
+        boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
+                || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
+        boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
+                || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
+        boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
+                || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
+        boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
+                || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
+        if (!contractedVisible) {
+            mContractedChild.setVisibility(View.INVISIBLE);
+        } else {
+            mContractedWrapper.setVisible(true);
+        }
+        if (mExpandedChild != null) {
+            if (!expandedVisible) {
+                mExpandedChild.setVisibility(View.INVISIBLE);
+            } else {
+                mExpandedWrapper.setVisible(true);
+            }
+        }
+        if (mHeadsUpChild != null) {
+            if (!headsUpVisible) {
+                mHeadsUpChild.setVisibility(View.INVISIBLE);
+            } else {
+                mHeadsUpWrapper.setVisible(true);
+            }
+        }
+        if (mSingleLineView != null) {
+            if (!singleLineVisible) {
+                mSingleLineView.setVisibility(View.INVISIBLE);
+            } else {
+                mSingleLineView.setVisible(true);
+            }
         }
     }
 
@@ -558,7 +584,7 @@
     private void animateToVisibleType(int visibleType) {
         final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
         final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
-        if (shownView == hiddenView) {
+        if (shownView == hiddenView || hiddenView == null) {
             shownView.setVisible(true);
             return;
         }
@@ -647,8 +673,9 @@
                 height = mContentHeight;
             }
             int expandedVisualType = getVisualTypeForHeight(height);
-            int collapsedVisualType = getVisualTypeForHeight(
-                    mContainingNotification.getCollapsedHeight());
+            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
+                    ? VISIBLE_TYPE_SINGLELINE
+                    : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
             return mTransformationStartVisibleType == collapsedVisualType
                     ? expandedVisualType
                     : collapsedVisualType;
@@ -762,7 +789,7 @@
             mHeadsUpWrapper.notifyContentUpdated(entry.notification);
         }
         updateShowingLegacyBackground();
-        selectLayout(false /* animate */, true /* force */);
+        mForceSelectNextLayout = true;
         setDark(mDark, false /* animate */, 0 /* delay */);
         mPreviousExpandedRemoteInputIntent = null;
         mPreviousHeadsUpRemoteInputIntent = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index f04fe5e..8207215 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -100,8 +100,17 @@
         boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
         boolean transformScale = transformScale();
         // lets animate the positions correctly
-        if (transformationAmount == 0.0f) {
-            int[] otherPosition = otherState.getLocationOnScreen();
+        if (transformationAmount == 0.0f
+                || transformX && getTransformationStartX() == UNDEFINED
+                || transformY && getTransformationStartY() == UNDEFINED
+                || transformScale && getTransformationStartScaleX() == UNDEFINED
+                || transformScale && getTransformationStartScaleY() == UNDEFINED) {
+            int[] otherPosition;
+            if (transformationAmount != 0.0f) {
+                otherPosition = otherState.getLaidOutLocationOnScreen();
+            } else {
+                otherPosition = otherState.getLocationOnScreen();
+            }
             int[] ownStablePosition = getLaidOutLocationOnScreen();
             if (customTransformation == null
                     || !customTransformation.initTransformation(this, otherState)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 0fdd99f..f3033cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -44,6 +44,8 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.RemoteInputController;
@@ -127,10 +129,14 @@
         mEditText.mShowImeOnInputConnection = false;
         mController.remoteInputSent(mEntry);
 
+        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
+                mEntry.notification.getPackageName());
         try {
             mPendingIntent.send(mContext, 0, fillInIntent);
         } catch (PendingIntent.CanceledException e) {
             Log.i(TAG, "Unable to send remote input result", e);
+            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
+                    mEntry.notification.getPackageName());
         }
     }
 
@@ -165,6 +171,8 @@
         mController.removeRemoteInput(mEntry);
         mEntry.remoteInputText = mEditText.getText();
         setVisibility(INVISIBLE);
+        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
+                mEntry.notification.getPackageName());
     }
 
     @Override
@@ -198,6 +206,9 @@
     }
 
     public void focus() {
+        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
+                mEntry.notification.getPackageName());
+
         setVisibility(VISIBLE);
         mController.addRemoteInput(mEntry);
         mEditText.setInnerFocusable(true);
@@ -252,6 +263,7 @@
             findScrollContainer();
             if (mScrollContainer != null) {
                 mScrollContainer.requestDisallowLongPress();
+                mScrollContainer.requestDisallowDismiss();
             }
         }
         return super.onInterceptTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 9603e81..0a4dce8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -232,6 +232,7 @@
      * animating.
      */
     private boolean mOnlyScrollingInThisMotion;
+    private boolean mDisallowDismissInThisMotion;
     private boolean mInterceptDelegateEnabled;
     private boolean mDelegateToScrollView;
     private boolean mDisallowScrollingInThisMotion;
@@ -1031,7 +1032,8 @@
         if (!mIsBeingDragged
                 && !mExpandingNotification
                 && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion) {
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
             horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
         }
         return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
@@ -2013,7 +2015,8 @@
         if (!mIsBeingDragged
                 && !mExpandingNotification
                 && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion) {
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
             swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
         }
         return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
@@ -2041,6 +2044,7 @@
             mExpandedInThisMotion = false;
             mOnlyScrollingInThisMotion = !mScroller.isFinished();
             mDisallowScrollingInThisMotion = false;
+            mDisallowDismissInThisMotion = false;
             mTouchIsClick = true;
             mInitialTouchX = ev.getX();
             mInitialTouchY = ev.getY();
@@ -2705,6 +2709,11 @@
         removeLongPressCallback();
     }
 
+    @Override
+    public void requestDisallowDismiss() {
+        mDisallowDismissInThisMotion = true;
+    }
+
     public void removeLongPressCallback() {
         mSwipeHelper.removeLongPressCallback();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java
index a35465e..64efa69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ScrollContainer.java
@@ -33,4 +33,9 @@
      * Request that the view is made visible by scrolling to it.
      */
     void scrollTo(View v);
+
+    /**
+     * Request that the view does not dismiss for the current touch.
+     */
+    void requestDisallowDismiss();
 }
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 2f79079..a3ea592 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -22,22 +22,28 @@
 import android.app.backup.BackupDataOutput;
 import android.app.backup.FullBackupDataOutput;
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.system.Os;
 import android.util.Slog;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 
 public class WallpaperBackupAgent extends BackupAgent {
     private static final String TAG = "WallpaperBackup";
     private static final boolean DEBUG = false;
 
     // NB: must be kept in sync with WallpaperManagerService but has no
-    // compile-time visiblity.
+    // compile-time visibility.
 
     // Target filenames within the system's wallpaper directory
     static final String WALLPAPER = "wallpaper_orig";
@@ -107,11 +113,7 @@
     }
 
     // We use the default onRestoreFile() implementation that will recreate our stage files,
-    // then postprocess in onRestoreFinished() to move them on top of the live data.
-    //
-    // NOTE: this relies on our local files dir being on the same filesystem as the live
-    // system wallpaper data.  If this is not the case then an actual copy operation will
-    // be needed.
+    // then post-process in onRestoreFinished() to apply the new wallpaper.
     @Override
     public void onRestoreFinished() {
         if (DEBUG) {
@@ -125,19 +127,25 @@
             // to back up the original image on the source device.
             if (imageStage.exists()) {
                 if (DEBUG) {
-                    Slog.v(TAG, "Got restored wallpaper; renaming into place");
+                    Slog.v(TAG, "Got restored wallpaper; applying");
                 }
-                // Rename the image file into place last because that is the trigger for
-                // the wallpaper observer to generate a new crop/scale
-                Os.rename(infoStage.getCanonicalPath(), mWallpaperInfo.getCanonicalPath());
-                Os.rename(imageStage.getCanonicalPath(), mWallpaperFile.getCanonicalPath());
+
+                // Parse the restored info file to find the crop hint.  Note that this currently
+                // relies on a priori knowledge of the wallpaper info file schema.
+                Rect cropHint = parseCropHint(infoStage);
+                if (cropHint != null) {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Restored crop hint " + cropHint + "; now writing data");
+                    }
+                    WallpaperManager wm = getSystemService(WallpaperManager.class);
+                    try (FileInputStream in = new FileInputStream(imageStage)) {
+                        wm.setStream(in, cropHint, true, WallpaperManager.FLAG_SYSTEM);
+                    } finally {} // auto-closes 'in'
+                }
             }
         } catch (Exception e) {
             Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
-            mWm.clearWallpaper(WallpaperManager.FLAG_SYSTEM, UserHandle.USER_SYSTEM);
         } finally {
-            // These "should" not exist because of the renames, but make sure
-            // in case of errors/exceptions/etc.
             if (DEBUG) {
                 Slog.v(TAG, "Removing restore stage files");
             }
@@ -146,8 +154,41 @@
         }
     }
 
+    private Rect parseCropHint(File wallpaperInfo) {
+        Rect cropHint = new Rect();
+        try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+            int type;
+            do {
+                type = parser.next();
+                if (type == XmlPullParser.START_TAG) {
+                    String tag = parser.getName();
+                    if ("wp".equals(tag)) {
+                        cropHint.left = getAttributeInt(parser, "cropLeft", 0);
+                        cropHint.top = getAttributeInt(parser, "cropTop", 0);
+                        cropHint.right = getAttributeInt(parser, "cropRight", 0);
+                        cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
+                    }
+                }
+            } while (type != XmlPullParser.END_DOCUMENT);
+        } catch (Exception e) {
+            // Whoops; can't process the info file at all.  Report failure.
+            Slog.w(TAG, "Failed to parse restored metadata: " + e.getMessage());
+            return null;
+        }
+
+        return cropHint;
+    }
+
+    private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
+        final String value = parser.getAttributeValue(null, name);
+        return (value == null) ? defValue : Integer.parseInt(value);
+    }
+
     //
-    // Key/value API: abstract, so required, but not used
+    // Key/value API: abstract, therefore required; but not used
     //
 
     @Override
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 7b3fd66..f49235c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2169,6 +2169,24 @@
     // User blacklisted an app for Data Saver mode; action pass package name of app.
     ACTION_DATA_SAVER_BLACKLIST = 396;
 
+    // User opened a remote input view associated with a notification. Passes package name of app
+    // that posted the notification. Note that this can also happen transiently during notification
+    // reinflation.
+    ACTION_REMOTE_INPUT_OPEN = 397;
+
+    // User attempt to send data through a remote input view associated with a notification.
+    // Passes package name of app that posted the notification. May succeed or fail.
+    ACTION_REMOTE_INPUT_SEND = 398;
+
+    // Failed attempt to send data through a remote input view associated with a
+    // notification. Passes package name of app that posted the notification.
+    ACTION_REMOTE_INPUT_FAIL = 399;
+
+    // User closed a remote input view associated with a notification. Passes package name of app
+    // that posted the notification. Note that this can also happen transiently during notification
+    // reinflation.
+    ACTION_REMOTE_INPUT_CLOSE = 400;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6288b56..95f9e2d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -7706,6 +7706,11 @@
         // when we're finished.
         private int mPmToken;
 
+        // When this is restore-during-install, we need to tell the package manager
+        // whether we actually launched the app, because this affects notifications
+        // around externally-visible state transitions.
+        private boolean mDidLaunch;
+
         // Is this a whole-system restore, i.e. are we establishing a new ancestral
         // dataset to base future restore-at-install operations from?
         private boolean mIsSystemRestore;
@@ -7769,6 +7774,7 @@
             mTargetPackage = targetPackage;
             mIsSystemRestore = isFullSystemRestore;
             mFinished = false;
+            mDidLaunch = false;
 
             if (targetPackage != null) {
                 // Single package restore
@@ -8149,6 +8155,9 @@
                 return;
             }
 
+            // Whatever happens next, we've launched the target app now; remember that.
+            mDidLaunch = true;
+
             // And then finally start the restore on this agent
             try {
                 initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
@@ -8430,6 +8439,10 @@
                     // Now we're really done with this one too
                     IoUtils.closeQuietly(mEnginePipes[0]);
 
+                    // In all cases we want to remember whether we launched
+                    // the target app as part of our work so far.
+                    mDidLaunch = (mEngine.getAgent() != null);
+
                     // If we hit a transport-level error, we are done with everything;
                     // if we hit an agent error we just go back to running the queue.
                     if (status == BackupTransport.TRANSPORT_OK) {
@@ -8522,7 +8535,7 @@
             if (mPmToken > 0) {
                 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
                 try {
-                    mPackageManagerBinder.finishPackageInstall(mPmToken);
+                    mPackageManagerBinder.finishPackageInstall(mPmToken, mDidLaunch);
                 } catch (RemoteException e) { /* can't happen */ }
             } else {
                 // We were invoked via an active restore session, not by the Package
@@ -9682,7 +9695,7 @@
             // Manager to proceed with the post-install handling for this package.
             if (DEBUG) Slog.v(TAG, "Finishing install immediately");
             try {
-                mPackageManagerBinder.finishPackageInstall(token);
+                mPackageManagerBinder.finishPackageInstall(token, false);
             } catch (RemoteException e) { /* can't happen */ }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index ac5c4ae..5096b35 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -192,11 +192,13 @@
                 }
             }
 
-            if (!mPendingIntents.isEmpty()) {
-                pw.println();
-                pw.println("Pending intents:");
-                for (PendingIntent pi : mPendingIntents) {
-                    pw.println(pi.toString());
+            synchronized (mPendingIntents) {
+                if (!mPendingIntents.isEmpty()) {
+                    pw.println();
+                    pw.println("Pending intents:");
+                    for (PendingIntent pi : mPendingIntents) {
+                        pw.println(pi.toString());
+                    }
                 }
             }
 
@@ -327,9 +329,9 @@
                         result[i++] = e;
                     }
                 }
-            }
 
-            reference.setValue(mLastEventReference);
+                reference.setValue(mLastEventReference);
+            }
 
             return result;
         }
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 03eb019..2103cce 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -352,6 +352,10 @@
         if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
                 + " from observer " + observer + ", flags " + Integer.toHexString(flags));
 
+        if (uri == null) {
+            throw new NullPointerException("Uri must not be null");
+        }
+
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
         final int callingUserHandle = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 50b8670..649a27c 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -22,9 +22,7 @@
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.IOtaDexopt;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.os.Environment;
 import android.os.RemoteException;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4b0eeed..89dc4fb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -498,8 +498,8 @@
 
     public static final int REASON_LAST = REASON_FORCED_DEXOPT;
 
-    // Special String to skip shared libraries check during compilation.
-    private static final String SPECIAL_SHARED_LIBRARY = "&";
+    /** Special library name that skips shared libraries check during compilation. */
+    private static final String SKIP_SHARED_LIBRARY_CHECK = "&";
 
     final ServiceThread mHandlerThread;
 
@@ -1570,6 +1570,7 @@
                     if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
 
                     PostInstallData data = mRunningInstalls.get(msg.arg1);
+                    final boolean didRestore = (msg.arg2 != 0);
                     mRunningInstalls.delete(msg.arg1);
 
                     if (data != null) {
@@ -1584,7 +1585,8 @@
 
                         // Handle the parent package
                         handlePackagePostInstall(parentRes, grantPermissions, killApp,
-                                grantedPermissions, args.observer);
+                                grantedPermissions, didRestore, args.installerPackageName,
+                                args.observer);
 
                         // Handle the child packages
                         final int childCount = (parentRes.addedChildPackages != null)
@@ -1592,7 +1594,8 @@
                         for (int i = 0; i < childCount; i++) {
                             PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
                             handlePackagePostInstall(childRes, grantPermissions, killApp,
-                                    grantedPermissions, args.observer);
+                                    grantedPermissions, false, args.installerPackageName,
+                                    args.observer);
                         }
 
                         // Log tracing if needed
@@ -1788,6 +1791,7 @@
 
     private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
             boolean killApp, String[] grantedPermissions,
+            boolean launchedForRestore, String installerPackage,
             IPackageInstallObserver2 installObserver) {
         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
             // Send the removed broadcasts
@@ -1872,6 +1876,14 @@
                             null /*package*/, null /*extras*/, 0 /*flags*/,
                             packageName /*targetPackage*/,
                             null /*finishedReceiver*/, updateUsers);
+                } else if (launchedForRestore && !isSystemApp(res.pkg)) {
+                    // First-install and we did a restore, so we're responsible for the
+                    // first-launch broadcast.
+                    if (DEBUG_BACKUP) {
+                        Slog.i(TAG, "Post-restore of " + packageName
+                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUsers));
+                    }
+                    sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers);
                 }
 
                 // Send broadcast package appeared if forward locked/external for all users
@@ -2336,7 +2348,7 @@
                                         dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
                                         getCompilerFilterForReason(REASON_SHARED_APK),
                                         StorageManager.UUID_PRIVATE_INTERNAL,
-                                        SPECIAL_SHARED_LIBRARY);
+                                        SKIP_SHARED_LIBRARY_CHECK);
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
@@ -11723,7 +11735,7 @@
     }
 
     @Override
-    public void finishPackageInstall(int token) {
+    public void finishPackageInstall(int token, boolean didLaunch) {
         enforceSystemOrRoot("Only the system is allowed to finish installs");
 
         if (DEBUG_INSTALL) {
@@ -11731,7 +11743,7 @@
         }
         Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
 
-        final Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
+        final Message msg = mHandler.obtainMessage(POST_INSTALL, token, didLaunch ? 1 : 0);
         mHandler.sendMessage(msg);
     }
 
@@ -12060,6 +12072,54 @@
         });
     }
 
+    /**
+     * Callback from PackageSettings whenever an app is first transitioned out of the
+     * 'stopped' state.  Normally we just issue the broadcast, but we can't do that if
+     * the app was "launched" for a restoreAtInstall operation.  Therefore we check
+     * here whether the app is the target of an ongoing install, and only send the
+     * broadcast immediately if it is not in that state.  If it *is* undergoing a restore,
+     * the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL
+     * handling.
+     */
+    void notifyFirstLaunch(final String pkgName, final String installerPackage, final int userId) {
+        // Serialize this with the rest of the install-process message chain.  In the
+        // restore-at-install case, this Runnable will necessarily run before the
+        // POST_INSTALL message is processed, so the contents of mRunningInstalls
+        // are coherent.  In the non-restore case, the app has already completed install
+        // and been launched through some other means, so it is not in a problematic
+        // state for observers to see the FIRST_LAUNCH signal.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < mRunningInstalls.size(); i++) {
+                    final PostInstallData data = mRunningInstalls.valueAt(i);
+                    if (pkgName.equals(data.res.pkg.applicationInfo.packageName)) {
+                        // right package; but is it for the right user?
+                        for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
+                            if (userId == data.res.newUsers[uIndex]) {
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "Package " + pkgName
+                                            + " being restored so deferring FIRST_LAUNCH");
+                                }
+                                return;
+                            }
+                        }
+                    }
+                }
+                // didn't find it, so not being restored
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "Package " + pkgName + " sending normal FIRST_LAUNCH");
+                }
+                sendFirstLaunchBroadcast(pkgName, installerPackage, new int[] {userId});
+            }
+        });
+    }
+
+    private void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds) {
+        sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
+                installerPkg, null, userIds);
+    }
+
     private abstract class HandlerParams {
         private static final int MAX_RETRIES = 4;
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f20a36e..1040f85 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4133,7 +4133,7 @@
         return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
     }
 
-    boolean setPackageStoppedStateLPw(PackageManagerService yucky, String packageName,
+    boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName,
             boolean stopped, boolean allowedByPermission, int uid, int userId) {
         int appId = UserHandle.getAppId(uid);
         final PackageSetting pkgSetting = mPackages.get(packageName);
@@ -4158,9 +4158,7 @@
             // pkgSetting.pkg.mSetStopped = stopped;
             if (pkgSetting.getNotLaunched(userId)) {
                 if (pkgSetting.installerPackageName != null) {
-                    yucky.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
-                            pkgSetting.name, null, 0,
-                            pkgSetting.installerPackageName, null, new int[] {userId});
+                    pm.notifyFirstLaunch(pkgSetting.name, pkgSetting.installerPackageName, userId);
                 }
                 pkgSetting.setNotLaunched(false, userId);
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6a20edb..534c661 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6290,9 +6290,11 @@
                         + maxLayer + " appToken=" + appToken);
                 for (int i = 0; i < windows.size(); i++) {
                     WindowState win = windows.get(i);
+                    WindowSurfaceController controller = win.mWinAnimator.mSurfaceController;
                     Slog.i(TAG_WM, win + ": " + win.mLayer
                             + " animLayer=" + win.mWinAnimator.mAnimLayer
-                            + " surfaceLayer=" + win.mWinAnimator.mSurfaceController.getLayer());
+                            + " surfaceLayer=" + ((controller == null)
+                                ? "null" : controller.getLayer()));
                 }
             }