Merge "Add a security warning to PdfRenderer doc." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 4dfbe63..6fdd1bc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2699,7 +2699,7 @@
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
     method public float getCenterX();
     method public float getCenterY();
-    method public android.graphics.Region getMagnifiedRegion();
+    method public android.graphics.Region getMagnificationRegion();
     method public float getScale();
     method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public boolean reset(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 3344b52..41b73eb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2810,7 +2810,7 @@
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
     method public float getCenterX();
     method public float getCenterY();
-    method public android.graphics.Region getMagnifiedRegion();
+    method public android.graphics.Region getMagnificationRegion();
     method public float getScale();
     method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public boolean reset(boolean);
diff --git a/api/test-current.txt b/api/test-current.txt
index a0ca0f4..d40d0b8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2699,7 +2699,7 @@
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
     method public float getCenterX();
     method public float getCenterY();
-    method public android.graphics.Region getMagnifiedRegion();
+    method public android.graphics.Region getMagnificationRegion();
     method public float getScale();
     method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public boolean reset(boolean);
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/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 56728ad..8b277b2 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -925,18 +925,25 @@
         }
 
         /**
-         * Returns the region of the screen currently being magnified. If
-         * magnification is not enabled, the returned region will be empty.
+         * Returns the region of the screen currently active for magnification. Changes to
+         * magnification scale and center only affect this portion of the screen. The rest of the
+         * screen, for example input methods, cannot be magnified. This region is relative to the
+         * unscaled screen and is independent of the scale and center point.
+         * <p>
+         * The returned region will be empty if magnification is not active. Magnification is active
+         * if magnification gestures are enabled or if a service is running that can control
+         * magnification.
          * <p>
          * <strong>Note:</strong> If the service is not yet connected (e.g.
          * {@link AccessibilityService#onServiceConnected()} has not yet been
          * called) or the service has been disconnected, this method will
          * return an empty region.
          *
-         * @return the screen-relative bounds of the magnified region
+         * @return the region of the screen currently active for magnification, or an empty region
+         * if magnification is not active.
          */
         @NonNull
-        public Region getMagnifiedRegion() {
+        public Region getMagnificationRegion() {
             final IAccessibilityServiceConnection connection =
                     AccessibilityInteractionClient.getInstance().getConnection(
                             mService.mConnectionId);
@@ -1049,11 +1056,12 @@
              * Called when the magnified region, scale, or center changes.
              *
              * @param controller the magnification controller
-             * @param region the new magnified region, may be empty if
-             *               magnification is not enabled (e.g. scale is 1)
+             * @param region the magnification region
              * @param scale the new scale
-             * @param centerX the new X coordinate around which magnification is focused
-             * @param centerY the new Y coordinate around which magnification is focused
+             * @param centerX the new X coordinate, in unscaled coordinates, around which
+             * magnification is focused
+             * @param centerY the new Y coordinate, in unscaled coordinates, around which
+             * magnification is focused
              */
             void onMagnificationChanged(@NonNull MagnificationController controller,
                     @NonNull Region region, float scale, float centerX, float centerY);
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index e18a34d..fc9581e 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.PathMeasure;
 import android.graphics.RectF;
@@ -26,10 +25,8 @@
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
-import android.view.ViewConfiguration;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -160,9 +157,10 @@
         private final List<StrokeDescription> mStrokes = new ArrayList<>();
 
         /**
-         * Add a stroke to the gesture description. Up to {@code MAX_STROKE_COUNT} paths may be
-         * added to a gesture, and the total gesture duration (earliest path start time to latest path
-         * end time) may not exceed {@code MAX_GESTURE_DURATION_MS}.
+         * Add a stroke to the gesture description. Up to
+         * {@link GestureDescription#getMaxStrokeCount()} paths may be
+         * added to a gesture, and the total gesture duration (earliest path start time to latest
+         * path end time) may not exceed {@link GestureDescription#getMaxGestureDuration()}.
          *
          * @param strokeDescription the stroke to add.
          *
@@ -201,10 +199,13 @@
         long mEndTime;
         private float mTimeToLengthConversion;
         private PathMeasure mPathMeasure;
+        // The tap location is only set for zero-length paths
+        float[] mTapLocation;
 
         /**
-         * @param path The path to follow. Must have exactly one contour, and that contour must
-         * have nonzero length. The bounds of the path must not be negative.
+         * @param path The path to follow. Must have exactly one contour. The bounds of the path
+         * must not be negative. The path must not be empty. If the path has zero length
+         * (for example, a single {@code moveTo()}), the stroke is a touch that doesn't move.
          * @param startTime The time, in milliseconds, from the time the gesture starts to the
          * time the stroke should start. Must not be negative.
          * @param duration The duration, in milliseconds, the stroke takes to traverse the path.
@@ -225,10 +226,18 @@
                     || (bounds.left < 0)) {
                 throw new IllegalArgumentException("Path bounds must not be negative");
             }
+            if (path.isEmpty()) {
+                throw new IllegalArgumentException("Path is empty");
+            }
             mPath = new Path(path);
             mPathMeasure = new PathMeasure(path, false);
             if (mPathMeasure.getLength() == 0) {
-                throw new IllegalArgumentException("Path has zero length");
+                // Treat zero-length paths as taps
+                Path tempPath = new Path(path);
+                tempPath.lineTo(-1, -1);
+                mTapLocation = new float[2];
+                PathMeasure pathMeasure = new PathMeasure(tempPath, false);
+                pathMeasure.getPosTan(0, mTapLocation, null);
             }
             if (mPathMeasure.nextContour()) {
                 throw new IllegalArgumentException("Path has more than one contour");
@@ -237,12 +246,10 @@
              * Calling nextContour has moved mPathMeasure off the first contour, which is the only
              * one we care about. Set the path again to go back to the first contour.
              */
-            mPathMeasure.setPath(path, false);
+            mPathMeasure.setPath(mPath, false);
             mStartTime = startTime;
             mEndTime = startTime + duration;
-            if (duration > 0) {
-                mTimeToLengthConversion = getLength() / duration;
-            }
+            mTimeToLengthConversion = getLength() / duration;
         }
 
         /**
@@ -278,6 +285,11 @@
 
         /* Assumes hasPointForTime returns true */
         boolean getPosForTime(long time, float[] pos) {
+            if (mTapLocation != null) {
+                pos[0] = mTapLocation[0];
+                pos[1] = mTapLocation[1];
+                return true;
+            }
             if (time == mEndTime) {
                 // Close to the end time, roundoff can be a problem
                 return mPathMeasure.getPosTan(getLength(), pos, null);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0764ff4..f7c0b4c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3479,7 +3479,9 @@
                     // the decor view we have to notify the view root that the
                     // callbacks may have changed.
                     ViewRootImpl impl = decor.getViewRootImpl();
-                    impl.notifyChildRebuilt();
+                    if (impl != null) {
+                        impl.notifyChildRebuilt();
+                    }
                 }
                 if (a.mVisibleFromClient && !a.mWindowAdded) {
                     a.mWindowAdded = true;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d7705b9..2a3e3d8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3351,7 +3351,8 @@
         }
 
         private void resetStandardTemplateWithActions(RemoteViews big) {
-            big.setViewVisibility(R.id.actions_container, View.GONE);
+            // actions_container is only reset when there are no actions to avoid focus issues with
+            // remote inputs.
             big.setViewVisibility(R.id.actions, View.GONE);
             big.removeAllViews(R.id.actions);
 
@@ -3396,6 +3397,8 @@
                     }
                     big.addView(R.id.actions, button);
                 }
+            } else {
+                big.setViewVisibility(R.id.actions_container, View.GONE);
             }
 
             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
@@ -4265,15 +4268,19 @@
         public RemoteViews makeBigContentView() {
 
             // Nasty
-            CharSequence oldBuilderContentText =
-                    mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
+            CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
 
             RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
 
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
+            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
 
             CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
+            if (TextUtils.isEmpty(bigTextText)) {
+                // In case the bigtext is null / empty fall back to the normal text to avoid a weird
+                // experience
+                bigTextText = mBuilder.processLegacyText(text);
+            }
             contentView.setTextViewText(R.id.big_text, bigTextText);
             contentView.setViewVisibility(R.id.big_text,
                     TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
@@ -4461,8 +4468,8 @@
             mAllowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES,
                     mAllowGeneratedReplies);
             Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
-            if (parcelables != null && parcelables instanceof Bundle[]) {
-                mMessages = Message.getMessagesFromBundleArray((Bundle[]) parcelables);
+            if (parcelables != null && parcelables instanceof Parcelable[]) {
+                mMessages = Message.getMessagesFromBundleArray(parcelables);
             }
         }
 
@@ -4558,6 +4565,25 @@
             return sb;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeHeadsUpContentView() {
+            Message m = findLatestIncomingMessage();
+            CharSequence title = mConversationTitle != null
+                    ? mConversationTitle
+                    : (m == null) ? null : m.mSender;
+            CharSequence text = (m == null)
+                    ? null
+                    : mConversationTitle != null ? makeMessageLine(m) : m.mText;
+
+            return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
+                    false /* hasProgress */,
+                    title,
+                    text);
+        }
+
         private static TextAppearanceSpan makeFontColorSpan(int color) {
             return new TextAppearanceSpan(null, 0, 0,
                     ColorStateList.valueOf(color), null);
@@ -4691,12 +4717,14 @@
                 return bundles;
             }
 
-            static List<Message> getMessagesFromBundleArray(Bundle[] bundles) {
+            static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
                 List<Message> messages = new ArrayList<>(bundles.length);
                 for (int i = 0; i < bundles.length; i++) {
-                    Message message = getMessageFromBundle(bundles[i]);
-                    if (message != null) {
-                        messages.add(message);
+                    if (bundles[i] instanceof Bundle) {
+                        Message message = getMessageFromBundle((Bundle)bundles[i]);
+                        if (message != null) {
+                            messages.add(message);
+                        }
                     }
                 }
                 return messages;
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index e9d82af..c5e0ea7 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -484,7 +484,7 @@
      */
     public void rollbackContentChanged() {
         if (mProcessingChange) {
-            mContentChanged = true;
+            onContentChanged();
         }
     }
 
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index a57fac3..2b8b28d 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -52,6 +52,9 @@
     void setRestrictBackground(boolean restrictBackground);
     boolean getRestrictBackground();
 
+    /** Callback used to change internal state on tethering */
+    void onTetheringChanged(String iface, boolean tethering);
+
     /** Control which applications can be exempt from background data restrictions */
     void addRestrictBackgroundWhitelistedUid(int uid);
     void removeRestrictBackgroundWhitelistedUid(int uid);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b9a3cff..2c63be2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1070,6 +1070,9 @@
         public int statSoftIrqTime;
         public int statIdlTime;
 
+        // Platform-level low power state stats
+        public String statPlatformIdleState;
+
         public HistoryStepDetails() {
             clear();
         }
@@ -1099,6 +1102,7 @@
             out.writeInt(statIrqTime);
             out.writeInt(statSoftIrqTime);
             out.writeInt(statIdlTime);
+            out.writeString(statPlatformIdleState);
         }
 
         public void readFromParcel(Parcel in) {
@@ -1119,6 +1123,7 @@
             statIrqTime = in.readInt();
             statSoftIrqTime = in.readInt();
             statIdlTime = in.readInt();
+            statPlatformIdleState = in.readString();
         }
     }
 
@@ -4788,6 +4793,8 @@
                             pw.print(sb);
                             pw.print(")");
                         }
+                        pw.print(", PlatformIdleStat ");
+                        pw.print(rec.stepDetails.statPlatformIdleState);
                         pw.println();
                     } else {
                         pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
@@ -4821,6 +4828,8 @@
                         pw.print(rec.stepDetails.statSoftIrqTime);
                         pw.print(',');
                         pw.print(rec.stepDetails.statIdlTime);
+                        pw.print(',');
+                        pw.print(rec.stepDetails.statPlatformIdleState);
                         pw.println();
                     }
                 }
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 3ad730b..4b188c4 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -54,13 +54,12 @@
     public interface MagnificationCallbacks {
 
         /**
-         * Called when the bounds of the screen content that is magnified changed.
-         * Note that not the entire screen is magnified.
+         * Called when the region where magnification operates changes. Note that this isn't the
+         * entire screen. For example, IMEs are not magnified.
          *
-         * @param magnifiedBounds the currently magnified region
-         * @param availableBounds the region available for magnification
+         * @param magnificationRegion the current magnification region
          */
-        public void onMagnifiedBoundsChanged(Region magnifiedBounds, Region availableBounds);
+        public void onMagnificationRegionChanged(Region magnificationRegion);
 
         /**
          * Called when an application requests a rectangle on the screen to allow
@@ -158,13 +157,11 @@
     public abstract void setMagnificationSpec(MagnificationSpec spec);
 
     /**
-     * Obtains the magnified and available regions.
+     * Obtains the magnification regions.
      *
-     * @param outMagnified the currently magnified region
-     * @param outAvailable the region available for magnification
+     * @param magnificationRegion the current magnification region
      */
-    public abstract void getMagnificationRegions(@NonNull Region outMagnified,
-            @NonNull Region outAvailable);
+    public abstract void getMagnificationRegion(@NonNull Region magnificationRegion);
 
     /**
      * Gets the magnification and translation applied to a window given its token.
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index bfc87f2..bb1ffcb 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1308,7 +1308,7 @@
 
         // Include the padding of the list
         int returnedHeight = mListPadding.top + mListPadding.bottom;
-        final int dividerHeight = ((mDividerHeight > 0) && mDivider != null) ? mDividerHeight : 0;
+        final int dividerHeight = mDividerHeight;
         // The previous height value that was less than maxHeight and contained
         // no partial children
         int prevHeightWithoutPartialChild = 0;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e69ed35..5358d78 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -108,7 +108,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 142 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 143 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -150,6 +150,13 @@
         public void batterySendBroadcast(Intent intent);
     }
 
+    public interface PlatformIdleStateCallback {
+        public String getPlatformLowPowerStats();
+    }
+
+    private final PlatformIdleStateCallback mPlatformIdleStateCallback;
+
+
     final class MyHandler extends Handler {
         public MyHandler(Looper looper) {
             super(looper, null, true);
@@ -569,6 +576,7 @@
         mDailyFile = null;
         mHandler = null;
         mExternalSync = null;
+        mPlatformIdleStateCallback = null;
         clearHistoryLocked();
     }
 
@@ -2220,6 +2228,12 @@
                     + cur.eventTag.string);
         }
         if (computeStepDetails) {
+            if (mPlatformIdleStateCallback != null) {
+                mCurHistoryStepDetails.statPlatformIdleState =
+                        mPlatformIdleStateCallback.getPlatformLowPowerStats();
+                if (DEBUG) Slog.i(TAG, "WRITE PlatformIdleState:" +
+                        mCurHistoryStepDetails.statPlatformIdleState);
+            }
             computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails);
             if (includeStepDetails != 0) {
                 mCurHistoryStepDetails.writeToParcel(dest);
@@ -7372,11 +7386,16 @@
     }
 
     public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) {
-        this(new SystemClocks(), systemDir, handler, externalSync);
+        this(new SystemClocks(), systemDir, handler, externalSync, null);
+    }
+
+    public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync,
+                            PlatformIdleStateCallback cb) {
+        this(new SystemClocks(), systemDir, handler, externalSync, cb);
     }
 
     public BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
-            ExternalStatsSync externalSync) {
+            ExternalStatsSync externalSync, PlatformIdleStateCallback cb) {
         init(clocks);
 
         if (systemDir != null) {
@@ -7462,6 +7481,7 @@
         initDischarge();
         clearHistoryLocked();
         updateDailyDeadlineLocked();
+        mPlatformIdleStateCallback = cb;
     }
 
     public BatteryStatsImpl(Parcel p) {
@@ -7477,6 +7497,7 @@
         mExternalSync = null;
         clearHistoryLocked();
         readFromParcel(p);
+        mPlatformIdleStateCallback = null;
     }
 
     public void setPowerProfile(PowerProfile profile) {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d36da9f..fb7a19f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2428,41 +2428,49 @@
 
     <!-- Phrase describing a time duration using minutes that is as short as possible, preferrably one character. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=6] -->
     <plurals name="duration_minutes_shortest">
+        <item quantity="one"><xliff:g example="1" id="count">%d</xliff:g>m</item>
         <item quantity="other"><xliff:g example="2" id="count">%d</xliff:g>m</item>
     </plurals>
 
     <!-- Phrase describing a time duration using hours that is as short as possible, preferrably one character. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=6] -->
     <plurals name="duration_hours_shortest">
+        <item quantity="one"><xliff:g example="1" id="count">%d</xliff:g>h</item>
         <item quantity="other"><xliff:g example="2" id="count">%d</xliff:g>h</item>
     </plurals>
 
     <!-- Phrase describing a time duration using days that is as short as possible, preferrably one character. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=6] -->
     <plurals name="duration_days_shortest">
+        <item quantity="one"><xliff:g example="1" id="count">%d</xliff:g>d</item>
         <item quantity="other"><xliff:g example="2" id="count">%d</xliff:g>d</item>
     </plurals>
 
     <!-- Phrase describing a time duration using years that is as short as possible, preferrably one character. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=6] -->
     <plurals name="duration_years_shortest">
+        <item quantity="one"><xliff:g example="1" id="count">%d</xliff:g>y</item>
         <item quantity="other"><xliff:g example="2" id="count">%d</xliff:g>y</item>
     </plurals>
 
     <!-- Phrase describing a time duration using minutes that is as short as possible, preferrably one character. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
     <plurals name="duration_minutes_shortest_future">
+        <item quantity="one">in <xliff:g example="1" id="count">%d</xliff:g>m</item>
         <item quantity="other">in <xliff:g example="2" id="count">%d</xliff:g>m</item>
     </plurals>
 
     <!-- Phrase describing a time duration using hours that is as short as possible, preferrably one character. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
     <plurals name="duration_hours_shortest_future">
+        <item quantity="one">in <xliff:g example="1" id="count">%d</xliff:g>h</item>
         <item quantity="other">in <xliff:g example="2" id="count">%d</xliff:g>h</item>
     </plurals>
 
     <!-- Phrase describing a time duration using days that is as short as possible, preferrably one character. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
     <plurals name="duration_days_shortest_future">
+        <item quantity="one">in <xliff:g example="1" id="count">%d</xliff:g>d</item>
         <item quantity="other">in <xliff:g example="2" id="count">%d</xliff:g>d</item>
     </plurals>
 
     <!-- Phrase describing a time duration using years that is as short as possible, preferrably one character. This version should be a future point in time. If the language needs a space in between the integer and the unit, please also integrate it in the string, but preferably it should not have a space in between.[CHAR LIMIT=14] -->
     <plurals name="duration_years_shortest_future">
+        <item quantity="one">in <xliff:g example="1" id="count">%d</xliff:g>y</item>
         <item quantity="other">in <xliff:g example="2" id="count">%d</xliff:g>y</item>
     </plurals>
 
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 28ed84f..907616c 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -44,6 +44,7 @@
         Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN,
         Key.QS_HOTSPOT_ADDED,
         Key.QS_DATA_SAVER_ADDED,
+        Key.QS_DATA_SAVER_DIALOG_SHOWN,
         Key.QS_INVERT_COLORS_ADDED,
         Key.QS_WORK_ADDED,
         Key.QS_NIGHT_ADDED,
@@ -63,6 +64,7 @@
         String TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN = "TvPictureInPictureOnboardingShown";
         String QS_HOTSPOT_ADDED = "QsHotspotAdded";
         String QS_DATA_SAVER_ADDED = "QsDataSaverAdded";
+        String QS_DATA_SAVER_DIALOG_SHOWN = "QsDataSaverDialogShown";
         String QS_INVERT_COLORS_ADDED = "QsInvertColorsAdded";
         String QS_WORK_ADDED = "QsWorkAdded";
         String QS_NIGHT_ADDED = "QsNightAdded";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index dded595..0ce805e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -14,12 +14,15 @@
 
 package com.android.systemui.qs.tiles;
 
+import android.content.DialogInterface;
 import android.content.Intent;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.DataSaverController;
 
 public class DataSaverTile extends QSTile<QSTile.BooleanState> implements
@@ -53,6 +56,29 @@
 
     @Override
     protected void handleClick() {
+        if (Prefs.getBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, false)) {
+            // Do it right away.
+            toggleDataSaver();
+            return;
+        }
+        // Shows dialog first
+        SystemUIDialog dialog = new SystemUIDialog(mContext);
+        dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
+        dialog.setMessage(com.android.internal.R.string.data_saver_description);
+        dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        toggleDataSaver();
+                    }
+                });
+        dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
+        dialog.setShowForAllUsers(true);
+        dialog.show();
+        Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
+    }
+
+    private void toggleDataSaver() {
         mState.value = !mDataSaverController.isDataSaverEnabled();
         MetricsLogger.action(mContext, getMetricsCategory(), mState.value);
         mDataSaverController.setDataSaverEnabled(mState.value);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 8239425..5b16fce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -277,6 +277,10 @@
         int intrinsicBefore = getIntrinsicHeight();
         mIsHeadsUp = isHeadsUp;
         mPrivateLayout.setHeadsUp(isHeadsUp);
+        if (mIsSummaryWithChildren) {
+            // The overflow might change since we allow more lines as HUN.
+            mChildrenContainer.updateGroupOverflow();
+        }
         if (intrinsicBefore != getIntrinsicHeight()) {
             notifyHeightChanged(false  /* needsAnimation */);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 7be50c4..81303fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
+import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
 import android.graphics.Rect;
@@ -118,6 +119,8 @@
     private int mTransformationStartVisibleType;
     private boolean mUserExpanding;
     private int mSingleLineWidthIndention;
+    private PendingIntent mPreviousExpandedRemoteInputIntent;
+    private PendingIntent mPreviousHeadsUpRemoteInputIntent;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -280,13 +283,19 @@
             mContractedChild.animate().cancel();
             removeView(mContractedChild);
         }
+        mPreviousExpandedRemoteInputIntent =
+                mExpandedRemoteInput != null ? mExpandedRemoteInput.getPendingIntent() : null;
         if (mExpandedChild != null) {
             mExpandedChild.animate().cancel();
             removeView(mExpandedChild);
+            mExpandedRemoteInput = null;
         }
+        mPreviousHeadsUpRemoteInputIntent =
+                mHeadsUpRemoteInput != null ? mHeadsUpRemoteInput.getPendingIntent() : null;
         if (mHeadsUpChild != null) {
             mHeadsUpChild.animate().cancel();
             removeView(mHeadsUpChild);
+            mHeadsUpRemoteInput = null;
         }
         mContractedChild = null;
         mExpandedChild = null;
@@ -496,6 +505,12 @@
         }
         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)
@@ -559,6 +574,19 @@
         });
     }
 
+    private void transferRemoteInputFocus(int visibleType) {
+        if (visibleType == VISIBLE_TYPE_HEADSUP
+                && mHeadsUpRemoteInput != null
+                && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
+            mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
+        }
+        if (visibleType == VISIBLE_TYPE_EXPANDED
+                && mExpandedRemoteInput != null
+                && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
+            mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
+        }
+    }
+
     /**
      * @param visibleType one of the static enum types in this view
      * @return the corresponding transformable view according to the given visible type
@@ -736,6 +764,8 @@
         updateShowingLegacyBackground();
         selectLayout(false /* animate */, true /* force */);
         setDark(mDark, false /* animate */, 0 /* delay */);
+        mPreviousExpandedRemoteInputIntent = null;
+        mPreviousHeadsUpRemoteInputIntent = null;
     }
 
     private void updateSingleLineView() {
@@ -771,19 +801,23 @@
 
         View bigContentView = mExpandedChild;
         if (bigContentView != null) {
-            mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput);
+            mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
+                    mPreviousExpandedRemoteInputIntent);
         } else {
             mExpandedRemoteInput = null;
         }
+
         View headsUpContentView = mHeadsUpChild;
         if (headsUpContentView != null) {
-            mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
+            mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
+                    mPreviousHeadsUpRemoteInputIntent);
         } else {
             mHeadsUpRemoteInput = null;
         }
     }
 
-    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) {
+    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+            boolean hasRemoteInput, PendingIntent existingPendingIntent) {
         View actionContainerCandidate = view.findViewById(
                 com.android.internal.R.id.actions_container);
         if (actionContainerCandidate instanceof FrameLayout) {
@@ -814,6 +848,24 @@
                 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
                         mContext.getColor(R.color.remote_input_text),
                         mContext.getColor(R.color.remote_input_hint)));
+
+                if (existingPendingIntent != null || existing.isActive()) {
+                    // The current action could be gone, or the pending intent no longer valid.
+                    // If we find a matching action in the new notification, focus, otherwise close.
+                    Notification.Action[] actions = entry.notification.getNotification().actions;
+                    if (existingPendingIntent != null) {
+                        existing.setPendingIntent(existingPendingIntent);
+                    }
+                    if (existing.updatePendingIntentFromActions(actions)) {
+                        if (!existing.isActive()) {
+                            existing.focus();
+                        }
+                    } else {
+                        if (existing.isActive()) {
+                            existing.close();
+                        }
+                    }
+                }
             }
             return existing;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
index 3c95a78..4ce330c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
@@ -52,7 +52,7 @@
         // This also clears the existing types
         super.updateTransformedTypes();
         if (mActions != null) {
-            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ACTIONS,
                     mActions);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 7a6d080..ddded49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -25,12 +25,14 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
  * Controls showing and hiding of the brightness mirror.
  */
 public class BrightnessMirrorController {
 
+    private final NotificationStackScrollLayout mStackScroller;
     public long TRANSITION_DURATION_OUT = 150;
     public long TRANSITION_DURATION_IN = 200;
 
@@ -45,10 +47,13 @@
         mScrimBehind = (ScrimView) statusBarWindow.findViewById(R.id.scrim_behind);
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
         mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
+        mStackScroller = (NotificationStackScrollLayout) statusBarWindow.findViewById(
+                R.id.notification_stack_scroller);
     }
 
     public void showMirror() {
         mBrightnessMirror.setVisibility(View.VISIBLE);
+        mStackScroller.setFadedOut(true);
         mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, Interpolators.ALPHA_OUT);
         outAnimation(mNotificationPanel.animate())
                 .withLayer();
@@ -62,6 +67,7 @@
                     @Override
                     public void run() {
                         mBrightnessMirror.setVisibility(View.INVISIBLE);
+                        mStackScroller.setFadedOut(false);
                     }
                 });
     }
@@ -69,7 +75,8 @@
     private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) {
         return a.alpha(0.0f)
                 .setDuration(TRANSITION_DURATION_OUT)
-                .setInterpolator(Interpolators.ALPHA_OUT);
+                .setInterpolator(Interpolators.ALPHA_OUT)
+                .withEndAction(null);
     }
     private ViewPropertyAnimator inAnimation(ViewPropertyAnimator a) {
         return a.alpha(1.0f)
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 ecd1772..f3033cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
@@ -43,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;
@@ -126,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());
         }
     }
 
@@ -164,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
@@ -197,6 +206,10 @@
     }
 
     public void focus() {
+        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
+                mEntry.notification.getPackageName());
+
+        setVisibility(VISIBLE);
         mController.addRemoteInput(mEntry);
         mEditText.setInnerFocusable(true);
         mEditText.mShowImeOnInputConnection = true;
@@ -250,6 +263,7 @@
             findScrollContainer();
             if (mScrollContainer != null) {
                 mScrollContainer.requestDisallowLongPress();
+                mScrollContainer.requestDisallowDismiss();
             }
         }
         return super.onInterceptTouchEvent(ev);
@@ -275,6 +289,63 @@
         }
     }
 
+    public boolean isActive() {
+        return mEditText.isFocused();
+    }
+
+    public void stealFocusFrom(RemoteInputView other) {
+        other.close();
+        setPendingIntent(other.mPendingIntent);
+        setRemoteInput(other.mRemoteInputs, other.mRemoteInput);
+        focus();
+    }
+
+    /**
+     * Tries to find an action in {@param actions} that matches the current pending intent
+     * of this view and updates its state to that of the found action
+     *
+     * @return true if a matching action was found, false otherwise
+     */
+    public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
+        boolean found = false;
+        if (mPendingIntent == null || actions == null) {
+            return false;
+        }
+        Intent current = mPendingIntent.getIntent();
+        if (current == null) {
+            return false;
+        }
+
+        for (Notification.Action a : actions) {
+            RemoteInput[] inputs = a.getRemoteInputs();
+            if (a.actionIntent == null || inputs == null) {
+                continue;
+            }
+            Intent candidate = a.actionIntent.getIntent();
+            if (!current.filterEquals(candidate)) {
+                continue;
+            }
+
+            RemoteInput input = null;
+            for (RemoteInput i : inputs) {
+                if (i.getAllowFreeFormInput()) {
+                    input = i;
+                }
+            }
+            if (input == null) {
+                continue;
+            }
+            setPendingIntent(a.actionIntent);
+            setRemoteInput(inputs, input);
+            return true;
+        }
+        return false;
+    }
+
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
     /**
      * An EditText that changes appearance based on whether it's focusable and becomes
      * un-focusable whenever the user navigates away from it or it becomes invisible.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index be98d7a..a1e89b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -139,7 +139,9 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             boolean isOverflow = i == overflowIndex;
-            child.setSingleLineWidthIndention(isOverflow ? mOverflowNumber.getMeasuredWidth() : 0);
+            child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null
+                    ? mOverflowNumber.getMeasuredWidth()
+                    : 0);
             child.measure(widthMeasureSpec, newHeightSpec);
             height += child.getMeasuredHeight();
 
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 3e0f930..0a4dce8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -23,6 +23,7 @@
 import android.animation.TimeAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.FloatRange;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -231,6 +232,7 @@
      * animating.
      */
     private boolean mOnlyScrollingInThisMotion;
+    private boolean mDisallowDismissInThisMotion;
     private boolean mInterceptDelegateEnabled;
     private boolean mDelegateToScrollView;
     private boolean mDisallowScrollingInThisMotion;
@@ -327,6 +329,8 @@
     };
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
     private boolean mPulsing;
+    private boolean mDrawBackgroundAsSrc;
+    private boolean mFadedOut;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -439,7 +443,12 @@
     }
 
     public void setDrawBackgroundAsSrc(boolean asSrc) {
-        mBackgroundPaint.setXfermode(asSrc ? mSrcMode : null);
+        mDrawBackgroundAsSrc = asSrc;
+        updateSrcDrawing();
+    }
+
+    private void updateSrcDrawing() {
+        mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadedOut ? mSrcMode : null);
         invalidate();
     }
 
@@ -1023,7 +1032,8 @@
         if (!mIsBeingDragged
                 && !mExpandingNotification
                 && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion) {
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
             horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
         }
         return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
@@ -1743,7 +1753,9 @@
     }
 
     private void applyCurrentBackgroundBounds() {
-        mScrimController.setExcludedBackgroundArea(mCurrentBounds);
+        if (!mFadedOut) {
+            mScrimController.setExcludedBackgroundArea(mCurrentBounds);
+        }
         invalidate();
     }
 
@@ -2003,7 +2015,8 @@
         if (!mIsBeingDragged
                 && !mExpandingNotification
                 && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion) {
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
             swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
         }
         return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
@@ -2031,6 +2044,7 @@
             mExpandedInThisMotion = false;
             mOnlyScrollingInThisMotion = !mScroller.isFinished();
             mDisallowScrollingInThisMotion = false;
+            mDisallowDismissInThisMotion = false;
             mTouchIsClick = true;
             mInitialTouchX = ev.getX();
             mInitialTouchY = ev.getY();
@@ -2695,6 +2709,11 @@
         removeLongPressCallback();
     }
 
+    @Override
+    public void requestDisallowDismiss() {
+        mDisallowDismissInThisMotion = true;
+    }
+
     public void removeLongPressCallback() {
         mSwipeHelper.removeLongPressCallback();
     }
@@ -3421,6 +3440,24 @@
         updateNotificationAnimationStates();
     }
 
+    public void setFadedOut(boolean fadingOut) {
+        if (fadingOut != mFadedOut) {
+            mFadedOut = fadingOut;
+            if (fadingOut) {
+                mScrimController.setExcludedBackgroundArea(null);
+            } else {
+                applyCurrentBackgroundBounds();
+            }
+            updateSrcDrawing();
+        }
+    }
+
+    @Override
+    public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
+        super.setAlpha(alpha);
+        setFadedOut(alpha != 1.0f);
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
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/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/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1b85016..fc2e95d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -151,7 +151,10 @@
 
     private static final int WINDOW_ID_UNKNOWN = -1;
 
-    private static int sIdCounter = 0;
+    // Each service has an ID. Also provide one for magnification gesture handling
+    public static final int MAGNIFICATION_GESTURE_HANDLER_ID = 0;
+
+    private static int sIdCounter = MAGNIFICATION_GESTURE_HANDLER_ID + 1;
 
     private static int sNextWindowId;
 
@@ -183,8 +186,6 @@
 
     private MagnificationController mMagnificationController;
 
-    private boolean mUnregisterMagnificationOnReset;
-
     private InteractionBridge mInteractionBridge;
 
     private AlertDialog mEnableTouchExplorationDialog;
@@ -784,11 +785,6 @@
             float scale, float centerX, float centerY) {
         synchronized (mLock) {
             notifyMagnificationChangedLocked(region, scale, centerX, centerY);
-
-            if (mUnregisterMagnificationOnReset && scale == 1.0f) {
-                mUnregisterMagnificationOnReset = false;
-                mMagnificationController.unregister();
-            }
         }
     }
 
@@ -1736,25 +1732,17 @@
     }
 
     private void updateMagnificationLocked(UserState userState) {
-        final int userId = userState.mUserId;
-        if (userId == mCurrentUserId && mMagnificationController != null) {
-            if (userState.mIsDisplayMagnificationEnabled ||
-                    userHasMagnificationServicesLocked(userState)) {
-                mMagnificationController.setUserId(userState.mUserId);
-            } else {
-                // If the user no longer has any magnification-controlling
-                // services and is not using magnification gestures, then
-                // reset the state to normal.
-                if (mMagnificationController.resetIfNeeded(true)) {
-                    // Animations are still running, so wait until we receive a
-                    // callback verifying that we've reset magnification.
-                    mUnregisterMagnificationOnReset = true;
-                } else {
-                    mUnregisterMagnificationOnReset = false;
-                    mMagnificationController.unregister();
-                    mMagnificationController = null;
-                }
-            }
+        if (userState.mUserId != mCurrentUserId) {
+            return;
+        }
+
+        if (userState.mIsDisplayMagnificationEnabled ||
+                userHasMagnificationServicesLocked(userState)) {
+            // Initialize the magnification controller if necessary
+            getMagnificationController();
+            mMagnificationController.register();
+        } else if (mMagnificationController != null) {
+            mMagnificationController.unregister();
         }
     }
 
@@ -2152,7 +2140,6 @@
         synchronized (mLock) {
             if (mMagnificationController == null) {
                 mMagnificationController = new MagnificationController(mContext, this, mLock);
-                mMagnificationController.register();
                 mMagnificationController.setUserId(mCurrentUserId);
             }
             return mMagnificationController;
@@ -2886,7 +2873,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 final Region region = Region.obtain();
-                getMagnificationController().getMagnifiedRegion(region);
+                getMagnificationController().getMagnificationRegion(region);
                 return region;
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -2957,7 +2944,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 return getMagnificationController().setScaleAndCenter(
-                        scale, centerX, centerY, animate);
+                        scale, centerX, centerY, animate, mId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3090,10 +3077,11 @@
                     userState.mInstalledServices.remove(mAccessibilityServiceInfo);
                     userState.mEnabledServices.remove(mComponentName);
                     userState.destroyUiAutomationService();
-                    if (readConfigurationForUserStateLocked(userState)) {
-                        onUserStateChangedLocked(userState);
-                    }
                 }
+                if (mId == getMagnificationController().getIdOfLastServiceToMagnify()) {
+                    getMagnificationController().resetIfNeeded(true);
+                }
+                onUserStateChangedLocked(userState);
             }
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index f1b3722..027b6e2 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -17,6 +17,7 @@
 package com.android.server.accessibility;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.SomeArgs;
 import com.android.server.LocalServices;
 
@@ -60,6 +61,8 @@
 
     private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1;
 
+    private static final int INVALID_ID = -1;
+
     private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
 
     private static final float MIN_SCALE = 1.0f;
@@ -80,9 +83,8 @@
      */
     private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
 
-    private final Region mMagnifiedRegion = Region.obtain();
-    private final Region mAvailableRegion = Region.obtain();
-    private final Rect mMagnifiedBounds = new Rect();
+    private final Region mMagnificationRegion = Region.obtain();
+    private final Rect mMagnificationBounds = new Rect();
 
     private final Rect mTempRect = new Rect();
     private final Rect mTempRect1 = new Rect();
@@ -97,35 +99,67 @@
 
     private int mUserId;
 
+    private int mIdOfLastServiceToMagnify = INVALID_ID;
+
+    // Flag indicating that we are registered with window manager.
+    private boolean mRegistered;
+
+    private boolean mUnregisterPending;
+
     public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
         mAms = ams;
         mContentResolver = context.getContentResolver();
         mScreenStateObserver = new ScreenStateObserver(context, this);
         mWindowStateObserver = new WindowStateObserver(context, this);
-        mSpecAnimationBridge = new SpecAnimationBridge(context);
         mLock = lock;
+        mSpecAnimationBridge = new SpecAnimationBridge(context, mLock);
     }
 
     /**
-     * Registers magnification-related observers.
+     * Start tracking the magnification region for services that control magnification and the
+     * magnification gesture handler.
+     *
+     * This tracking imposes a cost on the system, so we avoid tracking this data
+     * unless it's required.
      */
     public void register() {
-        mScreenStateObserver.register();
-        mWindowStateObserver.register();
-
-        // Obtain initial state.
-        mWindowStateObserver.getRegions(mMagnifiedRegion, mAvailableRegion);
-        mMagnifiedRegion.getBounds(mMagnifiedBounds);
+        synchronized (mLock) {
+            if (!mRegistered) {
+                mScreenStateObserver.register();
+                mWindowStateObserver.register();
+                mSpecAnimationBridge.setEnabled(true);
+                // Obtain initial state.
+                mWindowStateObserver.getMagnificationRegion(mMagnificationRegion);
+                mMagnificationRegion.getBounds(mMagnificationBounds);
+                mRegistered = true;
+            }
+        }
     }
 
     /**
-     * Unregisters magnification-related observers.
+     * Stop requiring tracking the magnification region. We may remain registered while we
+     * reset magnification.
      */
     public void unregister() {
-        mSpecAnimationBridge.cancel();
+        synchronized (mLock) {
+            if (!isMagnifying()) {
+                unregisterInternalLocked();
+            } else {
+                mUnregisterPending = true;
+                resetLocked(true);
+            }
+        }
+    }
 
-        mScreenStateObserver.unregister();
-        mWindowStateObserver.unregister();
+    private void unregisterInternalLocked() {
+        if (mRegistered) {
+            mSpecAnimationBridge.setEnabled(false);
+            mScreenStateObserver.unregister();
+            mWindowStateObserver.unregister();
+            mMagnificationRegion.setEmpty();
+            mRegistered = false;
+        }
+        mUnregisterPending = false;
     }
 
     /**
@@ -137,24 +171,22 @@
     }
 
     /**
-     * Sets the magnified and available regions.
+     * Update our copy of the current magnification region
      *
      * @param magnified the magnified region
-     * @param available the region available for magnification
      * @param updateSpec {@code true} to update the scale and center based on
      *                   the region bounds, {@code false} to leave them as-is
      */
-    private void setMagnifiedRegion(Region magnified, Region available, boolean updateSpec) {
+    private void onMagnificationRegionChanged(Region magnified, boolean updateSpec) {
         synchronized (mLock) {
             boolean magnificationChanged = false;
             boolean boundsChanged = false;
 
-            if (!mMagnifiedRegion.equals(magnified)) {
-                mMagnifiedRegion.set(magnified);
-                mMagnifiedRegion.getBounds(mMagnifiedBounds);
+            if (!mMagnificationRegion.equals(magnified)) {
+                mMagnificationRegion.set(magnified);
+                mMagnificationRegion.getBounds(mMagnificationBounds);
                 boundsChanged = true;
             }
-            mAvailableRegion.set(available);
             if (updateSpec) {
                 final MagnificationSpec sentSpec = mSpecAnimationBridge.mSentMagnificationSpec;
                 final float scale = sentSpec.scale;
@@ -162,12 +194,12 @@
                 final float offsetY = sentSpec.offsetY;
 
                 // Compute the new center and update spec as needed.
-                final float centerX = (mMagnifiedBounds.width() / 2.0f
-                        + mMagnifiedBounds.left - offsetX) / scale;
-                final float centerY = (mMagnifiedBounds.height() / 2.0f
-                        + mMagnifiedBounds.top - offsetY) / scale;
+                final float centerX = (mMagnificationBounds.width() / 2.0f
+                        + mMagnificationBounds.left - offsetX) / scale;
+                final float centerY = (mMagnificationBounds.height() / 2.0f
+                        + mMagnificationBounds.top - offsetY) / scale;
                 magnificationChanged = setScaleAndCenterLocked(
-                        scale, centerX, centerY, false);
+                        scale, centerX, centerY, false, INVALID_ID);
             }
 
             // If magnification changed we already notified for the change.
@@ -178,7 +210,7 @@
     }
 
     /**
-     * Returns whether the magnified region contains the specified
+     * Returns whether the magnification region contains the specified
      * screen-relative coordinates.
      *
      * @param x the screen-relative X coordinate to check
@@ -186,51 +218,36 @@
      * @return {@code true} if the coordinate is contained within the
      *         magnified region, or {@code false} otherwise
      */
-    public boolean magnifiedRegionContains(float x, float y) {
+    public boolean magnificationRegionContains(float x, float y) {
         synchronized (mLock) {
-            return mMagnifiedRegion.contains((int) x, (int) y);
-        }
-    }
-
-    /**
-     * Returns whether the region available for magnification contains the
-     * specified screen-relative coordinates.
-     *
-     * @param x the screen-relative X coordinate to check
-     * @param y the screen-relative Y coordinate to check
-     * @return {@code true} if the coordinate is contained within the
-     *         region available for magnification, or {@code false} otherwise
-     */
-    private boolean availableRegionContains(float x, float y) {
-        synchronized (mLock) {
-            return mAvailableRegion.contains((int) x, (int) y);
+            return mMagnificationRegion.contains((int) x, (int) y);
         }
     }
 
     /**
      * Populates the specified rect with the screen-relative bounds of the
-     * magnified region. If magnification is not enabled, the returned
+     * magnification region. If magnification is not enabled, the returned
      * bounds will be empty.
      *
      * @param outBounds rect to populate with the bounds of the magnified
      *                  region
      */
-    public void getMagnifiedBounds(@NonNull Rect outBounds) {
+    public void getMagnificationBounds(@NonNull Rect outBounds) {
         synchronized (mLock) {
-            outBounds.set(mMagnifiedBounds);
+            outBounds.set(mMagnificationBounds);
         }
     }
 
     /**
-     * Populates the specified region with the screen-relative magnified
+     * Populates the specified region with the screen-relative magnification
      * region. If magnification is not enabled, then the returned region
      * will be empty.
      *
      * @param outRegion the region to populate
      */
-    public void getMagnifiedRegion(@NonNull Region outRegion) {
+    public void getMagnificationRegion(@NonNull Region outRegion) {
         synchronized (mLock) {
-            outRegion.set(mMagnifiedRegion);
+            outRegion.set(mMagnificationRegion);
         }
     }
 
@@ -263,8 +280,8 @@
      */
     public float getCenterX() {
         synchronized (mLock) {
-            return  (mMagnifiedBounds.width() / 2.0f
-                    + mMagnifiedBounds.left - getOffsetX()) / getScale();
+            return (mMagnificationBounds.width() / 2.0f
+                    + mMagnificationBounds.left - getOffsetX()) / getScale();
         }
     }
 
@@ -286,8 +303,8 @@
      */
     public float getCenterY() {
         synchronized (mLock) {
-            return (mMagnifiedBounds.height() / 2.0f
-                    + mMagnifiedBounds.top - getOffsetY()) / getScale();
+            return (mMagnificationBounds.height() / 2.0f
+                    + mMagnificationBounds.top - getOffsetY()) / getScale();
         }
     }
 
@@ -335,17 +352,25 @@
      */
     public boolean reset(boolean animate) {
         synchronized (mLock) {
-            final MagnificationSpec spec = mCurrentMagnificationSpec;
-            final boolean changed = !spec.isNop();
-            if (changed) {
-                spec.clear();
-                onMagnificationChangedLocked();
-            }
-            mSpecAnimationBridge.updateSentSpec(spec, animate);
-            return changed;
+            return resetLocked(animate);
         }
     }
 
+    private boolean resetLocked(boolean animate) {
+        if (!mRegistered) {
+            return false;
+        }
+        final MagnificationSpec spec = mCurrentMagnificationSpec;
+        final boolean changed = !spec.isNop();
+        if (changed) {
+            spec.clear();
+            onMagnificationChangedLocked();
+        }
+        mIdOfLastServiceToMagnify = INVALID_ID;
+        mSpecAnimationBridge.updateSentSpec(spec, animate);
+        return changed;
+    }
+
     /**
      * Scales the magnified region around the specified pivot point,
      * optionally animating the transition. If animation is disabled, the
@@ -356,16 +381,20 @@
      * @param pivotY the screen-relative Y coordinate around which to scale
      * @param animate {@code true} to animate the transition, {@code false}
      *                to transition immediately
+     * @param id the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean setScale(float scale, float pivotX, float pivotY, boolean animate) {
+    public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) {
         synchronized (mLock) {
+            if (!mRegistered) {
+                return false;
+            }
             // Constrain scale immediately for use in the pivot calculations.
             scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
 
             final Rect viewport = mTempRect;
-            mMagnifiedRegion.getBounds(viewport);
+            mMagnificationRegion.getBounds(viewport);
             final MagnificationSpec spec = mCurrentMagnificationSpec;
             final float oldScale = spec.scale;
             final float oldCenterX = (viewport.width() / 2.0f - spec.offsetX) / oldScale;
@@ -376,7 +405,8 @@
             final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
             final float centerX = normPivotX + offsetX;
             final float centerY = normPivotY + offsetY;
-            return setScaleAndCenterLocked(scale, centerX, centerY, animate);
+            mIdOfLastServiceToMagnify = id;
+            return setScaleAndCenterLocked(scale, centerX, centerY, animate, id);
         }
     }
 
@@ -390,12 +420,16 @@
      *                center
      * @param animate {@code true} to animate the transition, {@code false}
      *                to transition immediately
+     * @param id the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean setCenter(float centerX, float centerY, boolean animate) {
+    public boolean setCenter(float centerX, float centerY, boolean animate, int id) {
         synchronized (mLock) {
-            return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate);
+            if (!mRegistered) {
+                return false;
+            }
+            return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate, id);
         }
     }
 
@@ -411,19 +445,27 @@
      *                center and scale, or {@link Float#NaN} to leave unchanged
      * @param animate {@code true} to animate the transition, {@code false}
      *                to transition immediately
+     * @param id the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean setScaleAndCenter(float scale, float centerX, float centerY, boolean animate) {
+    public boolean setScaleAndCenter(
+            float scale, float centerX, float centerY, boolean animate, int id) {
         synchronized (mLock) {
-            return setScaleAndCenterLocked(scale, centerX, centerY, animate);
+            if (!mRegistered) {
+                return false;
+            }
+            return setScaleAndCenterLocked(scale, centerX, centerY, animate, id);
         }
     }
 
     private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY,
-            boolean animate) {
+            boolean animate, int id) {
         final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
         mSpecAnimationBridge.updateSentSpec(mCurrentMagnificationSpec, animate);
+        if (isMagnifying() && (id != INVALID_ID)) {
+            mIdOfLastServiceToMagnify = id;
+        }
         return changed;
     }
 
@@ -432,22 +474,42 @@
      *
      * @param offsetX the amount in pixels to offset the X center
      * @param offsetY the amount in pixels to offset the Y center
+     * @param id the ID of the service requesting the change
      */
-    public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) {
+    public void offsetMagnifiedRegionCenter(float offsetX, float offsetY, int id) {
         synchronized (mLock) {
+            if (!mRegistered) {
+                return;
+            }
+
             final MagnificationSpec currSpec = mCurrentMagnificationSpec;
             final float nonNormOffsetX = currSpec.offsetX - offsetX;
             currSpec.offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
             final float nonNormOffsetY = currSpec.offsetY - offsetY;
             currSpec.offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0);
+            if (id != INVALID_ID) {
+                mIdOfLastServiceToMagnify = id;
+            }
             mSpecAnimationBridge.updateSentSpec(currSpec, false);
         }
     }
 
+    /**
+     * Get the ID of the last service that changed the magnification spec.
+     *
+     * @return The id
+     */
+    public int getIdOfLastServiceToMagnify() {
+        return mIdOfLastServiceToMagnify;
+    }
+
     private void onMagnificationChangedLocked() {
         mAms.onMagnificationStateChanged();
-        mAms.notifyMagnificationChanged(mMagnifiedRegion,
+        mAms.notifyMagnificationChanged(mMagnificationRegion,
                 getScale(), getCenterX(), getCenterY());
+        if (mUnregisterPending && !isMagnifying()) {
+            unregisterInternalLocked();
+        }
     }
 
     /**
@@ -503,8 +565,8 @@
             scale = getScale();
         }
 
-        // Ensure requested center is within the available region.
-        if (!availableRegionContains(centerX, centerY)) {
+        // Ensure requested center is within the magnification region.
+        if (!magnificationRegionContains(centerX, centerY)) {
             return false;
         }
 
@@ -518,16 +580,16 @@
             changed = true;
         }
 
-        final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f
-                + mMagnifiedBounds.left - centerX * scale;
+        final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f
+                + mMagnificationBounds.left - centerX * scale;
         final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
         if (Float.compare(currSpec.offsetX, offsetX) != 0) {
             currSpec.offsetX = offsetX;
             changed = true;
         }
 
-        final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f
-                + mMagnifiedBounds.top - centerY * scale;
+        final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f
+                + mMagnificationBounds.top - centerY * scale;
         final float offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0);
         if (Float.compare(currSpec.offsetY, offsetY) != 0) {
             currSpec.offsetY = offsetY;
@@ -542,12 +604,12 @@
     }
 
     private float getMinOffsetXLocked() {
-        final float viewportWidth = mMagnifiedBounds.width();
+        final float viewportWidth = mMagnificationBounds.width();
         return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
     }
 
     private float getMinOffsetYLocked() {
-        final float viewportHeight = mMagnifiedBounds.height();
+        final float viewportHeight = mMagnificationBounds.height();
         return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
     }
 
@@ -595,7 +657,7 @@
         final float scale = getSentScale();
         final float offsetX = getSentOffsetX();
         final float offsetY = getSentOffsetY();
-        getMagnifiedBounds(outFrame);
+        getMagnificationBounds(outFrame);
         outFrame.offset((int) -offsetX, (int) -offsetY);
         outFrame.scale(1.0f / scale);
     }
@@ -603,7 +665,7 @@
     private void requestRectangleOnScreen(int left, int top, int right, int bottom) {
         synchronized (mLock) {
             final Rect magnifiedFrame = mTempRect;
-            getMagnifiedBounds(magnifiedFrame);
+            getMagnificationBounds(magnifiedFrame);
             if (!magnifiedFrame.intersects(left, top, right, bottom)) {
                 return;
             }
@@ -640,7 +702,7 @@
             }
 
             final float scale = getScale();
-            offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale);
+            offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale, INVALID_ID);
         }
     }
 
@@ -656,7 +718,7 @@
 
         /**
          * The magnification spec that was sent to the window manager. This should
-         * only be accessed and modified on the main (e.g. animation) thread.
+         * only be accessed with the lock held.
          */
         private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
 
@@ -667,8 +729,13 @@
         private final ValueAnimator mTransformationAnimator;
 
         private final long mMainThreadId;
+        private final Object mLock;
 
-        private SpecAnimationBridge(Context context) {
+        @GuardedBy("mLock")
+        private boolean mEnabled = false;
+
+        private SpecAnimationBridge(Context context, Object lock) {
+            mLock = lock;
             final Looper mainLooper = context.getMainLooper();
             mMainThreadId = mainLooper.getThread().getId();
 
@@ -685,9 +752,19 @@
             mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
         }
 
-        public void cancel() {
-            if (mTransformationAnimator != null && mTransformationAnimator.isRunning()) {
-                mTransformationAnimator.cancel();
+        /**
+         * Enabled means the bridge will accept input. When not enabled, the output of the animator
+         * will be ignored
+         */
+        public void setEnabled(boolean enabled) {
+            synchronized (mLock) {
+                if (enabled != mEnabled) {
+                    mEnabled = enabled;
+                    if (!mEnabled) {
+                        mSentMagnificationSpec.clear();
+                        mWindowManager.setMagnificationSpec(mSentMagnificationSpec);
+                    }
+                }
             }
         }
 
@@ -710,28 +787,32 @@
             }
 
             // If the current and sent specs don't match, update the sent spec.
-            final boolean changed = !mSentMagnificationSpec.equals(spec);
-            if (changed) {
-                if (animate) {
-                    animateMagnificationSpec(spec);
-                } else {
-                    setMagnificationSpec(spec);
+            synchronized (mLock) {
+                final boolean changed = !mSentMagnificationSpec.equals(spec);
+                if (changed) {
+                    if (animate) {
+                        animateMagnificationSpecLocked(spec);
+                    } else {
+                        setMagnificationSpecLocked(spec);
+                    }
                 }
             }
         }
 
-        private void animateMagnificationSpec(MagnificationSpec toSpec) {
+        private void animateMagnificationSpecLocked(MagnificationSpec toSpec) {
             mTransformationAnimator.setObjectValues(mSentMagnificationSpec, toSpec);
             mTransformationAnimator.start();
         }
 
-        private void setMagnificationSpec(MagnificationSpec spec) {
-            if (DEBUG_SET_MAGNIFICATION_SPEC) {
-                Slog.i(LOG_TAG, "Sending: " + spec);
-            }
+        private void setMagnificationSpecLocked(MagnificationSpec spec) {
+            if (mEnabled) {
+                if (DEBUG_SET_MAGNIFICATION_SPEC) {
+                    Slog.i(LOG_TAG, "Sending: " + spec);
+                }
 
-            mSentMagnificationSpec.setTo(spec);
-            mWindowManager.setMagnificationSpec(spec);
+                mSentMagnificationSpec.setTo(spec);
+                mWindowManager.setMagnificationSpec(spec);
+            }
         }
 
         private class UpdateHandler extends Handler {
@@ -759,12 +840,16 @@
 
             @Override
             public MagnificationSpec get(SpecAnimationBridge object) {
-                return object.mSentMagnificationSpec;
+                synchronized (object.mLock) {
+                    return object.mSentMagnificationSpec;
+                }
             }
 
             @Override
             public void set(SpecAnimationBridge object, MagnificationSpec value) {
-                object.setMagnificationSpec(value);
+                synchronized (object.mLock) {
+                    object.setMagnificationSpecLocked(value);
+                }
             }
         }
 
@@ -862,15 +947,14 @@
         }
 
         @Override
-        public void onMagnifiedBoundsChanged(Region magnified, Region available) {
+        public void onMagnificationRegionChanged(Region magnificationRegion) {
             final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = Region.obtain(magnified);
-            args.arg2 = Region.obtain(available);
+            args.arg1 = Region.obtain(magnificationRegion);
             mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
         }
 
-        private void handleOnMagnifiedBoundsChanged(Region magnified, Region available) {
-            mController.setMagnifiedRegion(magnified, available, mSpecIsDirty);
+        private void handleOnMagnifiedBoundsChanged(Region magnificationRegion) {
+            mController.onMagnificationRegionChanged(magnificationRegion, mSpecIsDirty);
             mSpecIsDirty = false;
         }
 
@@ -911,8 +995,15 @@
             mController.resetIfNeeded(true);
         }
 
-        public void getRegions(@NonNull Region outMagnified, @NonNull Region outAvailable) {
-            mWindowManager.getMagnificationRegions(outMagnified, outAvailable);
+        /**
+         * This method is used to get the magnification region in the tiny time slice between
+         * registering the callbacks and handling the message.
+         * TODO: Elimiante this extra path, perhaps by processing the message immediately
+         *
+         * @param outMagnificationRegion
+         */
+        public void getMagnificationRegion(@NonNull Region outMagnificationRegion) {
+            mWindowManager.getMagnificationRegion(outMagnificationRegion);
         }
 
         private class CallbackHandler extends Handler {
@@ -926,10 +1017,8 @@
                     case MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED: {
                         final SomeArgs args = (SomeArgs) message.obj;
                         final Region magnifiedBounds = (Region) args.arg1;
-                        final Region availableBounds = (Region) args.arg2;
-                        handleOnMagnifiedBoundsChanged(magnifiedBounds, availableBounds);
+                        handleOnMagnifiedBoundsChanged(magnifiedBounds);
                         magnifiedBounds.recycle();
-                        availableBounds.recycle();
                     } break;
                     case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
                         final SomeArgs args = (SomeArgs) message.obj;
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 818ac81..39bc809 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -237,7 +237,7 @@
         final float eventX = event.getX();
         final float eventY = event.getY();
         if (mMagnificationController.isMagnifying()
-                && mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
+                && mMagnificationController.magnificationRegionContains(eventX, eventY)) {
             final float scale = mMagnificationController.getScale();
             final float scaledOffsetX = mMagnificationController.getOffsetX();
             final float scaledOffsetY = mMagnificationController.getOffsetY();
@@ -381,7 +381,8 @@
                 Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
                         + " scrollY: " + distanceY);
             }
-            mMagnificationController.offsetMagnifiedRegionCenter(distanceX, distanceY);
+            mMagnificationController.offsetMagnifiedRegionCenter(distanceX, distanceY,
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             return true;
         }
 
@@ -421,7 +422,8 @@
 
             final float pivotX = detector.getFocusX();
             final float pivotY = detector.getFocusY();
-            mMagnificationController.setScale(scale, pivotX, pivotY, false);
+            mMagnificationController.setScale(scale, pivotX, pivotY, false,
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             return true;
         }
 
@@ -469,14 +471,14 @@
                     }
                     final float eventX = event.getX();
                     final float eventY = event.getY();
-                    if (mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
+                    if (mMagnificationController.magnificationRegionContains(eventX, eventY)) {
                         if (mLastMoveOutsideMagnifiedRegion) {
                             mLastMoveOutsideMagnifiedRegion = false;
-                            mMagnificationController.setCenter(eventX,
-                                    eventY, true);
+                            mMagnificationController.setCenter(eventX, eventY, true,
+                                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
                         } else {
-                            mMagnificationController.setCenter(eventX,
-                                    eventY, false);
+                            mMagnificationController.setCenter(eventX, eventY, false,
+                                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
                         }
                     } else {
                         mLastMoveOutsideMagnifiedRegion = true;
@@ -571,7 +573,7 @@
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
                     mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
-                    if (!mMagnificationController.magnifiedRegionContains(
+                    if (!mMagnificationController.magnificationRegionContains(
                             event.getX(), event.getY())) {
                         transitionToDelegatingStateAndClear();
                         return;
@@ -616,7 +618,7 @@
                         return;
                     }
                     mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
-                    if (!mMagnificationController.magnifiedRegionContains(
+                    if (!mMagnificationController.magnificationRegionContains(
                             event.getX(), event.getY())) {
                         transitionToDelegatingStateAndClear();
                         return;
@@ -726,7 +728,8 @@
             if (!mMagnificationController.isMagnifying()) {
                 final float targetScale = mMagnificationController.getPersistedScale();
                 final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE);
-                mMagnificationController.setScaleAndCenter(scale, up.getX(), up.getY(), true);
+                mMagnificationController.setScaleAndCenter(scale, up.getX(), up.getY(), true,
+                        AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             } else {
                 mMagnificationController.reset(true);
             }
@@ -742,7 +745,8 @@
 
             final float targetScale = mMagnificationController.getPersistedScale();
             final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE);
-            mMagnificationController.setScaleAndCenter(scale, down.getX(), down.getY(), true);
+            mMagnificationController.setScaleAndCenter(scale, down.getX(), down.getY(), true,
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
 
             transitionToState(STATE_VIEWPORT_DRAGGING);
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f06583b..0287332 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -31,7 +31,6 @@
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
-
 import android.annotation.Nullable;
 import android.app.BroadcastOptions;
 import android.app.Notification;
@@ -1382,6 +1381,10 @@
             if (LOGD_RULES) {
                 log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
             }
+            if (restrictBackground) {
+                log("onRestrictBackgroundChanged(true): disabling tethering");
+                mTethering.untetherAll();
+            }
         }
 
         @Override
@@ -1813,6 +1816,14 @@
         pw.decreaseIndent();
         pw.println();
 
+        pw.println("Metered Interfaces:");
+        pw.increaseIndent();
+        for (String value : mMeteredIfaces) {
+            pw.println(value);
+        }
+        pw.decreaseIndent();
+        pw.println();
+
         pw.println("Network Requests:");
         pw.increaseIndent();
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -2568,7 +2579,14 @@
     public int tether(String iface) {
         ConnectivityManager.enforceTetherChangePermission(mContext);
         if (isTetheringSupported()) {
-            return mTethering.tether(iface);
+            final int status = mTethering.tether(iface);
+            if (status == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                try {
+                    mPolicyManager.onTetheringChanged(iface, true);
+                } catch (RemoteException e) {
+                }
+            }
+            return status;
         } else {
             return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
         }
@@ -2579,7 +2597,14 @@
         ConnectivityManager.enforceTetherChangePermission(mContext);
 
         if (isTetheringSupported()) {
-            return mTethering.untether(iface);
+            final int status = mTethering.untether(iface);
+            if (status == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                try {
+                    mPolicyManager.onTetheringChanged(iface, false);
+                } catch (RemoteException e) {
+                }
+            }
+            return status;
         } else {
             return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
         }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2516f5d..c6786de 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -79,7 +79,8 @@
  * battery life.
  */
 public final class BatteryStatsService extends IBatteryStats.Stub
-        implements PowerManagerInternal.LowPowerModeListener {
+        implements PowerManagerInternal.LowPowerModeListener,
+        BatteryStatsImpl.PlatformIdleStateCallback {
     static final String TAG = "BatteryStatsService";
 
     /**
@@ -173,6 +174,33 @@
         }
     }
 
+    private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
+    private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
+                    .newDecoder()
+                    .onMalformedInput(CodingErrorAction.REPLACE)
+                    .onUnmappableCharacter(CodingErrorAction.REPLACE)
+                    .replaceWith("?");
+    private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE);
+    private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
+    private static final int MAX_LOW_POWER_STATS_SIZE = 512;
+
+    @Override
+    public String getPlatformLowPowerStats() {
+        mUtf8BufferStat.clear();
+        mUtf16BufferStat.clear();
+        mDecoderStat.reset();
+        int bytesWritten = getPlatformLowPowerStats(mUtf8BufferStat);
+        if (bytesWritten < 0) {
+            return null;
+        } else if (bytesWritten == 0) {
+            return "Empty";
+        }
+        mUtf8BufferStat.limit(bytesWritten);
+        mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
+        mUtf16BufferStat.flip();
+        return mUtf16BufferStat.toString();
+    }
+
     BatteryStatsService(File systemDir, Handler handler) {
         // Our handler here will be accessing the disk, use a different thread than
         // what the ActivityManagerService gave us (no I/O on that one!).
@@ -182,9 +210,9 @@
         mHandler = new BatteryStatsHandler(thread.getLooper());
 
         // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
-        mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
+        mStats = new BatteryStatsImpl(systemDir, handler, mHandler, this);
     }
-    
+
     public void publish(Context context) {
         mContext = context;
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 75d49c3..cdb68d8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -220,6 +220,7 @@
 
     private void finishUserBoot(UserState uss, IIntentReceiver resultTo) {
         final int userId = uss.mHandle.getIdentifier();
+        Slog.d(TAG, "Finishing user boot " + userId);
         synchronized (mService) {
             // Bail if we ended up with a stale user
             if (mStartedUsers.get(userId) != uss) return;
@@ -248,7 +249,8 @@
                             + "): attempting unlock because parent is unlocked");
                     maybeUnlockUser(userId);
                 } else {
-                    Slog.d(TAG, "User " + userId + " (parent " + parent.id
+                    String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
+                    Slog.d(TAG, "User " + userId + " (parent " + parentId
                             + "): delaying unlock because parent is locked");
                 }
             } else {
@@ -333,6 +335,10 @@
         synchronized (mService) {
             // Bail if we ended up with a stale user
             if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
+            final UserInfo userInfo = getUserInfo(userId);
+            if (userInfo == null) {
+                return;
+            }
 
             // Only keep marching forward if user is actually unlocked
             if (!isUserKeyUnlocked(userId)) return;
@@ -341,6 +347,25 @@
                 // Remember that we logged in
                 mUserManager.onUserLoggedIn(userId);
 
+                if (!userInfo.isInitialized()) {
+                    if (userId != UserHandle.USER_SYSTEM) {
+                        Slog.d(TAG, "Initializing user #" + userId);
+                        Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+                        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                        mService.broadcastIntentLocked(null, null, intent, null,
+                                new IIntentReceiver.Stub() {
+                                    @Override
+                                    public void performReceive(Intent intent, int resultCode,
+                                            String data, Bundle extras, boolean ordered,
+                                            boolean sticky, int sendingUser) {
+                                        // Note: performReceive is called with mService lock held
+                                        getUserManager().makeInitialized(userInfo.id);
+                                    }
+                                }, 0, null, null, null, AppOpsManager.OP_NONE,
+                                null, true, false, MY_PID, SYSTEM_UID, userId);
+                    }
+                }
+                Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
                 final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                 bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -669,6 +694,35 @@
         }
     }
 
+    /**
+     * Start user, if its not already running.
+     * <p>The user will be brought to the foreground, if {@code foreground} parameter is set.
+     * When starting the user, multiple intents will be broadcast in the following order:</p>
+     * <ul>
+     *     <li>{@link Intent#ACTION_USER_STARTED} - sent to registered receivers of the new user
+     *     <li>{@link Intent#ACTION_USER_BACKGROUND} - sent to registered receivers of the outgoing
+     *     user and all profiles of this user. Sent only if {@code foreground} parameter is true
+     *     <li>{@link Intent#ACTION_USER_FOREGROUND} - sent to registered receivers of the new
+     *     user and all profiles of this user. Sent only if {@code foreground} parameter is true
+     *     <li>{@link Intent#ACTION_USER_SWITCHED} - sent to registered receivers of the new user.
+     *     Sent only if {@code foreground} parameter is true
+     *     <li>{@link Intent#ACTION_USER_STARTING} - ordered broadcast sent to registered receivers
+     *     of the new fg user
+     *     <li>{@link Intent#ACTION_LOCKED_BOOT_COMPLETED} - ordered broadcast sent to receivers of
+     *     the new user
+     *     <li>{@link Intent#ACTION_USER_UNLOCKED} - sent to registered receivers of the new user
+     *     <li>{@link Intent#ACTION_PRE_BOOT_COMPLETED} - ordered broadcast sent to receivers of the
+     *     new user. Sent only when the user is booting after a system update.
+     *     <li>{@link Intent#ACTION_USER_INITIALIZE} - ordered broadcast sent to receivers of the
+     *     new user. Sent only the first time a user is starting.
+     *     <li>{@link Intent#ACTION_BOOT_COMPLETED} - ordered broadcast sent to receivers of the new
+     *     user. Indicates that the user has finished booting.
+     * </ul>
+     *
+     * @param userId ID of the user to start
+     * @param foreground true if user should be brought to the foreground
+     * @return true if the user has been successfully started
+     */
     boolean startUser(final int userId, final boolean foreground) {
         if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -680,7 +734,7 @@
             throw new SecurityException(msg);
         }
 
-        if (DEBUG_MU) Slog.i(TAG, "starting userid:" + userId + " fore:" + foreground);
+        Slog.i(TAG, "Starting userid:" + userId + " fg:" + foreground);
 
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -790,36 +844,8 @@
                             null, false, false, MY_PID, SYSTEM_UID, userId);
                 }
 
-                if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
-                    if (userId != UserHandle.USER_SYSTEM) {
-                        Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
-                        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                        mService.broadcastIntentLocked(null, null, intent, null,
-                                new IIntentReceiver.Stub() {
-                                    @Override
-                                    public void performReceive(Intent intent, int resultCode,
-                                            String data, Bundle extras, boolean ordered,
-                                            boolean sticky, int sendingUser) {
-                                        mHandler.post(new Runnable() {
-                                            @Override
-                                            public void run() {
-                                                onUserInitialized(uss, foreground,
-                                                        oldUserId, userId);
-                                            }
-                                        });
-                                    }
-                                }, 0, null, null, null, AppOpsManager.OP_NONE,
-                                null, true, false, MY_PID, SYSTEM_UID, userId);
-                        uss.initializing = true;
-                    } else {
-                        getUserManager().makeInitialized(userInfo.id);
-                    }
-                }
-
                 if (foreground) {
-                    if (!uss.initializing) {
-                        moveUserToForegroundLocked(uss, oldUserId, userId);
-                    }
+                    moveUserToForegroundLocked(uss, oldUserId, userId);
                 } else {
                     mService.mUserController.finishUserBoot(uss);
                 }
@@ -996,8 +1022,8 @@
         }
     }
 
-    void dispatchUserSwitch(final UserState uss, final int oldUserId,
-            final int newUserId) {
+    void dispatchUserSwitch(final UserState uss, final int oldUserId, final int newUserId) {
+        Slog.d(TAG, "Dispatch onUserSwitching oldUser #" + oldUserId + " newUser #" + newUserId);
         final int observerCount = mUserSwitchObservers.beginBroadcast();
         if (observerCount > 0) {
             final IRemoteCallback callback = new IRemoteCallback.Stub() {
@@ -1041,39 +1067,14 @@
     }
 
     void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
-        completeSwitchAndInitialize(uss, oldUserId, newUserId, false, true);
-    }
-
-    void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) {
+        Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId);
         synchronized (mService) {
-            if (foreground) {
-                moveUserToForegroundLocked(uss, oldUserId, newUserId);
-            }
+            mService.mWindowManager.stopFreezingScreen();
         }
-        completeSwitchAndInitialize(uss, oldUserId, newUserId, true, false);
-    }
-
-    void completeSwitchAndInitialize(UserState uss, int oldUserId, int newUserId,
-            boolean clearInitializing, boolean clearSwitching) {
-        boolean unfrozen = false;
-        synchronized (mService) {
-            if (clearInitializing) {
-                uss.initializing = false;
-                getUserManager().makeInitialized(uss.mHandle.getIdentifier());
-            }
-            if (clearSwitching) {
-                uss.switching = false;
-            }
-            if (!uss.switching && !uss.initializing) {
-                mService.mWindowManager.stopFreezingScreen();
-                unfrozen = true;
-            }
-        }
-        if (unfrozen) {
-            mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
-            mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
-                    newUserId, 0));
-        }
+        uss.switching = false;
+        mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
+        mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
+                newUserId, 0));
         stopGuestOrEphemeralUserIfBackground();
         stopBackgroundUsersIfEnforced(oldUserId);
     }
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 56abd95..952283e 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -54,7 +54,6 @@
     public int state = STATE_BOOTING;
     public int lastState = STATE_BOOTING;
     public boolean switching;
-    public boolean initializing;
 
     /**
      * The last time that a provider was reported to usage stats as being brought to important
@@ -103,7 +102,6 @@
         pw.print(prefix);
         pw.print("state="); pw.print(stateToString(state));
         if (switching) pw.print(" SWITCHING");
-        if (initializing) pw.print(" INITIALIZING");
         pw.println();
     }
 }
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/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index c2022d5..1012f9a 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -610,6 +610,13 @@
         return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
 
+    public void untetherAll() {
+        if (DBG) Log.d(TAG, "Untethering " + mIfaces);
+        for (String iface : mIfaces.keySet()) {
+            untether(iface);
+        }
+    }
+
     public int getLastTetherError(String iface) {
         TetherInterfaceSM sm = null;
         synchronized (mPublicSync) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 43f47fa..0b1ece5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1899,6 +1899,18 @@
     }
 
     @Override
+    public void onTetheringChanged(String iface, boolean tethering) {
+        // No need to enforce permission because setRestrictBackground() will do it.
+        if (LOGD) Log.d(TAG, "onTetherStateChanged(" + iface + ", " + tethering + ")");
+        synchronized (mRulesLock) {
+            if (mRestrictBackground && tethering) {
+                Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
+                setRestrictBackground(false);
+            }
+        }
+    }
+
+    @Override
     public void setRestrictBackground(boolean restrictBackground) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
         final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 3ef077b..101f56f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -124,9 +124,9 @@
         }
     }
 
-    public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) {
+    public void getMagnificationRegionLocked(Region outMagnificationRegion) {
         if (mDisplayMagnifier != null) {
-            mDisplayMagnifier.getMagnificationRegionsLocked(outMagnified, outAvailable);
+            mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
         }
     }
 
@@ -400,8 +400,8 @@
             return spec;
         }
 
-        public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) {
-            mMagnifedViewport.getBoundsLocked(outMagnified, outAvailable);
+        public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+            mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
         }
 
         public void destroyLocked() {
@@ -424,10 +424,8 @@
 
             private final Matrix mTempMatrix = new Matrix();
 
-            private final Region mMagnifiedBounds = new Region();
-            private final Region mAvailableBounds = new Region();
-            private final Region mOldMagnifiedBounds = new Region();
-            private final Region mOldAvailableBounds = new Region();
+            private final Region mMagnificationRegion = new Region();
+            private final Region mOldMagnificationRegion = new Region();
 
             private final Path mCircularPath;
 
@@ -463,10 +461,8 @@
                 recomputeBoundsLocked();
             }
 
-            public void getBoundsLocked(@NonNull Region outMagnified,
-                    @NonNull Region outAvailable) {
-                outMagnified.set(mMagnifiedBounds);
-                outAvailable.set(mAvailableBounds);
+            public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
+                outMagnificationRegion.set(mMagnificationRegion);
             }
 
             public void updateMagnificationSpecLocked(MagnificationSpec spec) {
@@ -488,11 +484,12 @@
                 final int screenWidth = mTempPoint.x;
                 final int screenHeight = mTempPoint.y;
 
-                mMagnifiedBounds.set(0, 0, 0, 0);
-                mAvailableBounds.set(0, 0, screenWidth, screenHeight);
+                mMagnificationRegion.set(0, 0, 0, 0);
+                final Region availableBounds = mTempRegion1;
+                availableBounds.set(0, 0, screenWidth, screenHeight);
 
                 if (mCircularPath != null) {
-                    mAvailableBounds.setPath(mCircularPath, mAvailableBounds);
+                    availableBounds.setPath(mCircularPath, availableBounds);
                 }
 
                 Region nonMagnifiedBounds = mTempRegion4;
@@ -526,21 +523,21 @@
                             (int) windowFrame.right, (int) windowFrame.bottom);
                     // Only update new regions
                     Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
-                    portionOfWindowAlreadyAccountedFor.set(mMagnifiedBounds);
+                    portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
                     portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
                     windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
 
                     if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
-                        mMagnifiedBounds.op(windowBounds, Region.Op.UNION);
-                        mMagnifiedBounds.op(mAvailableBounds, Region.Op.INTERSECT);
+                        mMagnificationRegion.op(windowBounds, Region.Op.UNION);
+                        mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
                     } else {
                         nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
-                        mAvailableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+                        availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
                     }
 
                     // Update accounted bounds
                     Region accountedBounds = mTempRegion2;
-                    accountedBounds.set(mMagnifiedBounds);
+                    accountedBounds.set(mMagnificationRegion);
                     accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
                     accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
 
@@ -556,43 +553,36 @@
 
                 visibleWindows.clear();
 
-                mMagnifiedBounds.op(mDrawBorderInset, mDrawBorderInset,
+                mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
                         screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
                         Region.Op.INTERSECT);
 
-                final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(mMagnifiedBounds);
-                final boolean availableChanged = !mOldAvailableBounds.equals(mAvailableBounds);
-                if (magnifiedChanged || availableChanged) {
-                    if (magnifiedChanged) {
-                        mWindow.setBounds(mMagnifiedBounds);
-                        Rect dirtyRect = mTempRect1;
-                        if (mFullRedrawNeeded) {
-                            mFullRedrawNeeded = false;
-                            dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
-                                    screenWidth - mDrawBorderInset,
-                                    screenHeight - mDrawBorderInset);
-                            mWindow.invalidate(dirtyRect);
-                        } else {
-                            Region dirtyRegion = mTempRegion3;
-                            dirtyRegion.set(mMagnifiedBounds);
-                            dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
-                            dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
-                            dirtyRegion.getBounds(dirtyRect);
-                            mWindow.invalidate(dirtyRect);
-                        }
-
-                        mOldMagnifiedBounds.set(mMagnifiedBounds);
+                final boolean magnifiedChanged =
+                        !mOldMagnificationRegion.equals(mMagnificationRegion);
+                if (magnifiedChanged) {
+                    mWindow.setBounds(mMagnificationRegion);
+                    final Rect dirtyRect = mTempRect1;
+                    if (mFullRedrawNeeded) {
+                        mFullRedrawNeeded = false;
+                        dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
+                                screenWidth - mDrawBorderInset,
+                                screenHeight - mDrawBorderInset);
+                        mWindow.invalidate(dirtyRect);
+                    } else {
+                        final Region dirtyRegion = mTempRegion3;
+                        dirtyRegion.set(mMagnificationRegion);
+                        dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
+                        dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+                        dirtyRegion.getBounds(dirtyRect);
+                        mWindow.invalidate(dirtyRect);
                     }
 
-                    if (availableChanged) {
-                        mOldAvailableBounds.set(mAvailableBounds);
-                    }
-
+                    mOldMagnificationRegion.set(mMagnificationRegion);
                     final SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = Region.obtain(mMagnifiedBounds);
-                    args.arg2 = Region.obtain(mAvailableBounds);
+                    args.arg1 = Region.obtain(mMagnificationRegion);
                     mHandler.obtainMessage(
-                            MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
+                            MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
+                            .sendToTarget();
                 }
             }
 
@@ -616,14 +606,14 @@
             public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
                 if (shown) {
                     mFullRedrawNeeded = true;
-                    mOldMagnifiedBounds.set(0, 0, 0, 0);
+                    mOldMagnificationRegion.set(0, 0, 0, 0);
                 }
                 mWindow.setShown(shown, animate);
             }
 
             public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
                 MagnificationSpec spec = mMagnificationSpec;
-                mMagnifiedBounds.getBounds(rect);
+                mMagnificationRegion.getBounds(rect);
                 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
                 rect.scale(1.0f / spec.scale);
             }
@@ -886,7 +876,7 @@
         }
 
         private class MyHandler extends Handler {
-            public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
+            public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
             public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
             public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
             public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
@@ -899,13 +889,11 @@
             @Override
             public void handleMessage(Message message) {
                 switch (message.what) {
-                    case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
+                    case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
                         final SomeArgs args = (SomeArgs) message.obj;
                         final Region magnifiedBounds = (Region) args.arg1;
-                        final Region availableBounds = (Region) args.arg2;
-                        mCallbacks.onMagnifiedBoundsChanged(magnifiedBounds, availableBounds);
+                        mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
                         magnifiedBounds.recycle();
-                        availableBounds.recycle();
                     } break;
 
                     case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cf415ff..6a20edb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3625,8 +3625,7 @@
             // disregarding font scale, which should remain set to
             // the value of the previous configuration.
             mTempConfiguration.setToDefaults();
-            mTempConfiguration.fontScale = currentConfig.fontScale;
-            mTempConfiguration.uiMode = currentConfig.uiMode;
+            mTempConfiguration.updateFrom(currentConfig);
             computeScreenConfigurationLocked(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
                 mWaitingForConfig = true;
@@ -3689,11 +3688,15 @@
         }
 
         synchronized(mWindowMap) {
-            mCurConfiguration = new Configuration(config);
             if (mWaitingForConfig) {
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
+            boolean configChanged = mCurConfiguration.diff(config) != 0;
+            if (!configChanged) {
+                return null;
+            }
+            mCurConfiguration = new Configuration(config);
             return onConfigurationChanged();
         }
     }
@@ -8877,8 +8880,7 @@
 
         boolean configChanged = updateOrientationFromAppTokensLocked(false);
         mTempConfiguration.setToDefaults();
-        mTempConfiguration.fontScale = mCurConfiguration.fontScale;
-        mTempConfiguration.uiMode = mCurConfiguration.uiMode;
+        mTempConfiguration.updateFrom(mCurConfiguration);
         computeScreenConfigurationLocked(mTempConfiguration);
         configChanged |= mCurConfiguration.diff(mTempConfiguration) != 0;
 
@@ -10975,12 +10977,10 @@
         }
 
         @Override
-        public void getMagnificationRegions(@NonNull Region outMagnified,
-                @NonNull Region outAvailable) {
+        public void getMagnificationRegion(@NonNull Region magnificationRegion) {
             synchronized (mWindowMap) {
                 if (mAccessibilityController != null) {
-                    mAccessibilityController.getMagnificationRegionsLocked(
-                            outMagnified, outAvailable);
+                    mAccessibilityController.getMagnificationRegionLocked(magnificationRegion);
                 } else {
                     throw new IllegalStateException("Magnification callbacks not set!");
                 }
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 5c43659..183a370 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -27,8 +27,10 @@
 #include <utils/misc.h>
 #include <utils/Log.h>
 #include <hardware/hardware.h>
+#include <hardware/power.h>
 #include <suspend/autosuspend.h>
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -47,6 +49,7 @@
 
 static bool wakeup_init = false;
 static sem_t wakeup_sem;
+extern struct power_module* gPowerModule;
 
 static void wakeup_callback(bool success)
 {
@@ -170,8 +173,122 @@
     return mergedreasonpos - mergedreason;
 }
 
+static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
+    int num_modes = -1;
+    char *output = (char*)env->GetDirectBufferAddress(outBuf), *offset = output;
+    int remaining = (int)env->GetDirectBufferCapacity(outBuf);
+    power_state_platform_sleep_state_t *list;
+    size_t *voter_list;
+    int total_added = -1;
+
+    if (outBuf == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", "null argument");
+        goto error;
+    }
+
+    if (!gPowerModule) {
+        ALOGE("%s: gPowerModule not loaded", POWER_HARDWARE_MODULE_ID);
+        goto error;
+    }
+
+    if (! (gPowerModule->get_platform_low_power_stats && gPowerModule->get_number_of_platform_modes
+       && gPowerModule->get_voter_list)) {
+        ALOGE("%s: Missing API", POWER_HARDWARE_MODULE_ID);
+        goto error;
+    }
+
+    if (gPowerModule->get_number_of_platform_modes) {
+        num_modes = gPowerModule->get_number_of_platform_modes(gPowerModule);
+    }
+
+    if (num_modes < 1) {
+        ALOGE("%s: Platform does not even have one low power mode", POWER_HARDWARE_MODULE_ID);
+        goto error;
+    }
+
+    list = (power_state_platform_sleep_state_t *)calloc(num_modes,
+        sizeof(power_state_platform_sleep_state_t));
+    if (!list) {
+        ALOGE("%s: power_state_platform_sleep_state_t allocation failed", POWER_HARDWARE_MODULE_ID);
+        goto error;
+    }
+
+    voter_list = (size_t *)calloc(num_modes, sizeof(*voter_list));
+    if (!voter_list) {
+        ALOGE("%s: voter_list allocation failed", POWER_HARDWARE_MODULE_ID);
+        goto err_free;
+    }
+
+    gPowerModule->get_voter_list(gPowerModule, voter_list);
+
+    for (int i = 0; i < num_modes; i++) {
+        list[i].voters = (power_state_voter_t *)calloc(voter_list[i],
+                         sizeof(power_state_voter_t));
+        if (!list[i].voters) {
+            ALOGE("%s: voter_t allocation failed", POWER_HARDWARE_MODULE_ID);
+            goto err_free;
+        }
+    }
+
+    if (!gPowerModule->get_platform_low_power_stats(gPowerModule, list)) {
+        for (int i = 0; i < num_modes; i++) {
+            int added;
+
+            added = snprintf(offset, remaining, "%s_time=%" PRIu64 " %s_count=%" PRIu64 " ",
+                    list[i].name, list[i].residency_in_msec_since_boot, list[i].name,
+                    list[i].total_transitions);
+            if (added < 0) {
+                break;
+            }
+            if (added > remaining) {
+                added = remaining;
+            }
+            offset += added;
+            remaining -= added;
+            total_added += added;
+
+            for (unsigned int j = 0; j < list[i].number_of_voters; j++) {
+                added = snprintf(offset, remaining, "%s_time=%" PRIu64 " %s_count=%" PRIu64 " ",
+                        list[i].voters[j].name,
+                        list[i].voters[j].total_time_in_msec_voted_for_since_boot,
+                        list[i].voters[j].name,
+                        list[i].voters[j].total_number_of_times_voted_since_boot);
+                if (added < 0) {
+                    break;
+                }
+                if (added > remaining) {
+                    added = remaining;
+                }
+                offset += added;
+                remaining -= added;
+                total_added += added;
+            }
+
+            if (remaining <= 0) {
+                /* rewrite NULL character*/
+                offset--;
+                total_added--;
+                ALOGE("%s module: buffer not enough", POWER_HARDWARE_MODULE_ID);
+                break;
+            }
+        }
+    }
+    *offset = 0;
+    total_added += 1;
+
+err_free:
+    for (int i = 0; i < num_modes; i++) {
+        free(list[i].voters);
+    }
+    free(list);
+    free(voter_list);
+error:
+    return total_added;
+}
+
 static const JNINativeMethod method_table[] = {
     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
+    { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
 };
 
 int register_android_server_BatteryStatsService(JNIEnv *env)
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 2fdb8e2..cbbfda6a 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -48,7 +48,7 @@
 // ----------------------------------------------------------------------------
 
 static jobject gPowerManagerServiceObj;
-static struct power_module* gPowerModule;
+struct power_module* gPowerModule;
 
 static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1];