Merge "Fix lint errors around import lines in IInputConnectionWrapper"
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 66a2600d..d8d4a6e 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -23,8 +23,7 @@
 
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal.CallSession;
-
-import java.util.Random;
+import com.android.internal.os.CachedDeviceState;
 
 import org.junit.After;
 import org.junit.Before;
@@ -32,8 +31,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertNull;
-
 
 /**
  * Performance tests for {@link BinderCallsStats}
@@ -49,6 +46,8 @@
     @Before
     public void setUp() {
         mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+        CachedDeviceState deviceState = new CachedDeviceState(false, false);
+        mBinderCallsStats.setDeviceState(deviceState.getReadonlyClient());
     }
 
     @After
diff --git a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
new file mode 100644
index 0000000..0f880b7
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Performance tests for {@link LooperStats}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LooperStatsPerfTest {
+    private static final int DISTINCT_MESSAGE_COUNT = 1000;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    private LooperStats mStats;
+    private CachedDeviceState mDeviceState;
+    private HandlerThread mThread;
+    private Message[] mMessages = new Message[DISTINCT_MESSAGE_COUNT];
+
+    @Before
+    public void setUp() {
+        mStats = new LooperStats(1, DISTINCT_MESSAGE_COUNT - 1);
+        mDeviceState = new CachedDeviceState(false, false);
+        mStats.setDeviceState(mDeviceState.getReadonlyClient());
+        // The tests are all single-threaded. HandlerThread is created to allow creating Handlers.
+        mThread = new HandlerThread("UnusedThread");
+        mThread.start();
+        for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+            mMessages[i] = mThread.getThreadHandler().obtainMessage(i);
+        }
+    }
+
+    @After
+    public void tearDown() {
+        mThread.quit();
+    }
+
+    @Test
+    public void timeHundredPercentSampling() {
+        mStats.setSamplingInterval(1);
+        runScenario();
+    }
+
+    @Test
+    public void timeOnePercentSampling() {
+        mStats.setSamplingInterval(100);
+        runScenario();
+    }
+
+    @Test
+    public void timeCollectionDisabled() {
+        // We do not collect data on charger.
+        mDeviceState.setCharging(true);
+        runScenario();
+    }
+
+    private void runScenario() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+                Object token = mStats.messageDispatchStarting();
+                mStats.messageDispatched(token, mMessages[i]);
+            }
+        }
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index a3b5c7aaea..6ad1663 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -11313,6 +11313,7 @@
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
+    field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
@@ -11322,6 +11323,7 @@
     field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
+    field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
     field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
     field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
     field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -13984,6 +13986,8 @@
     method public float getRunAdvance(char[], int, int, int, int, boolean, int);
     method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
     method public android.graphics.Shader getShader();
+    method public float getStrikeThruPosition();
+    method public float getStrikeThruThickness();
     method public android.graphics.Paint.Cap getStrokeCap();
     method public android.graphics.Paint.Join getStrokeJoin();
     method public float getStrokeMiter();
@@ -14004,6 +14008,9 @@
     method public int getTextWidths(java.lang.String, int, int, float[]);
     method public int getTextWidths(java.lang.String, float[]);
     method public android.graphics.Typeface getTypeface();
+    method public float getUnderlinePosition();
+    method public float getUnderlineThickness();
+    method public float getWordSpacing();
     method public android.graphics.Xfermode getXfermode();
     method public boolean hasGlyph(java.lang.String);
     method public final boolean isAntiAlias();
@@ -14055,6 +14062,7 @@
     method public void setTextSkewX(float);
     method public android.graphics.Typeface setTypeface(android.graphics.Typeface);
     method public void setUnderlineText(boolean);
+    method public void setWordSpacing(float);
     method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
     field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
     field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
@@ -15135,9 +15143,9 @@
   public class StateListDrawable extends android.graphics.drawable.DrawableContainer {
     ctor public StateListDrawable();
     method public void addState(int[], android.graphics.drawable.Drawable);
+    method public int findStateDrawableIndex(int[]);
     method public int getStateCount();
     method public android.graphics.drawable.Drawable getStateDrawable(int);
-    method public int getStateDrawableIndex(int[]);
     method public int[] getStateSet(int);
   }
 
@@ -45984,6 +45992,7 @@
     method public float getDimension(android.util.DisplayMetrics);
     method public final float getFloat();
     method public float getFraction(float, float);
+    method public boolean isColorType();
     method public void setTo(android.util.TypedValue);
     field public static final int COMPLEX_MANTISSA_MASK = 16777215; // 0xffffff
     field public static final int COMPLEX_MANTISSA_SHIFT = 8; // 0x8
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3c9f7ee..2ecfbe7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -133,7 +133,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10024
+    // Next: 10025
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -159,6 +159,7 @@
         Temperature temperature = 10021;
         BinderCalls binder_calls = 10022;
         BinderCallsExceptions binder_calls_exceptions = 10023;
+        LooperStats looper_stats = 10024;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -2203,3 +2204,50 @@
     // Total number of exceptions.
     optional int64 exception_count = 2;
 }
+
+message LooperStats {
+    // Currently not collected and always set to 0.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // Fully qualified class name of the handler target class.
+    //
+    // This field does not contain PII. This is a system server class name.
+    optional string handler_class_name = 2;
+
+    // The name of the thread that runs the Looper.
+    //
+    // This field does not contain PII. This is a system server thread name.
+    optional string looper_thread_name = 3;
+
+    // The name of the dispatched message.
+    //
+    // This field does not contain PII. This is a system server constant or class
+    // name.
+    optional string message_name = 4;
+
+    // Total number of successfully dispatched messages.
+    optional int64 message_count = 5;
+
+    // Total number of messages that failed dispatching.
+    optional int64 exception_count = 6;
+
+    // Total number of processed messages we have data recorded for. If we
+    // collected data for all the messages, message_count will be equal to
+    // recorded_message_count.
+    //
+    // If recorded_message_count is different than message_count, it means data
+    // collection has been sampled. All the fields below will be sampled in this
+    // case.
+    optional int64 recorded_message_count = 7;
+
+    // Total latency of all processed messages.
+    // Average can be computed using recorded_total_latency_micros /
+    // recorded_message_count.
+    optional int64 recorded_total_latency_micros = 8;
+
+    // Total CPU usage of all processed message.
+    // Average can be computed using recorded_total_cpu_micros /
+    // recorded_message_count. Total can be computed using
+    // recorded_total_cpu_micros / recorded_message_count * call_count.
+    optional int64 recorded_total_cpu_micros = 9;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index e44351b..42cc543 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -105,9 +105,9 @@
 
     readPipe.reset();  // Close the read end (owned by the child process).
 
-    // Using fopen() because fwrite() has the right logic to chunking write()
+    // Using fdopen() because fwrite() has the right logic to chunking write()
     // over a pipe (see __sfvwrite()).
-    FILE* writePipeStream = fdopen(writePipe.get(), "wb");
+    FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb");
     if (!writePipeStream) {
         ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
         return false;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index e6e8455..2871882 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -183,7 +183,13 @@
          {{},
           {},
           1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}
+          new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+        // looper_stats
+        {android::util::LOOPER_STATS,
+         {{5, 6, 7, 8, 9},
+          {2, 3, 4},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::LOOPER_STATS)}}
         };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 02719e3f8..45e38cf 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -152,8 +152,8 @@
 Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
 Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
 Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;Landroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;)Z
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
 Landroid/view/IWindowManager;->endProlongedAnimations()V
 Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
 Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1108f93..225b6cf 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -615,6 +615,13 @@
     public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
 
     /**
+     * Value for {@link #privateFlags}: whether this app is signed with the
+     * platform key.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
+    /**
      * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * google partition of the system image.
      * @hide
@@ -622,11 +629,11 @@
     public static final int PRIVATE_FLAG_PRODUCT_SERVICES = 1 << 21;
 
     /**
-     * Value for {@link #privateFlags}: whether this app is signed with the
-     * platform key.
+     * Indicates whether this package requires access to non-SDK APIs.
+     * Only system apps and tests are allowed to use this property.
      * @hide
      */
-    public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+    public static final int PRIVATE_FLAG_USES_NON_SDK_API = 1 << 22;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -1009,13 +1016,6 @@
     public String appComponentFactory;
 
     /**
-     * Indicates whether this package requires access to non-SDK APIs. Only system apps
-     * and tests are allowed to use this property.
-     * @hide
-     */
-    public boolean usesNonSdkApi;
-
-    /**
      * The category of this app. Categories are used to cluster multiple apps
      * together into meaningful groups, such as when summarizing battery,
      * network, or disk usage. Apps should only define this value when they fit
@@ -1294,6 +1294,7 @@
                 pw.println(prefix + "category=" + category);
             }
             pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
+            pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi());
         }
         super.dumpBack(pw, prefix);
     }
@@ -1718,11 +1719,18 @@
         return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
     }
 
+    /**
+     * @hide
+     */
+    public boolean usesNonSdkApi() {
+        return (privateFlags & PRIVATE_FLAG_USES_NON_SDK_API) != 0;
+    }
+
     private boolean isAllowedToUseHiddenApis() {
         if (isSignedWithPlatformKey()) {
             return true;
         } else if (isSystemApp() || isUpdatedSystemApp()) {
-            return usesNonSdkApi || isPackageWhitelistedForHiddenApis();
+            return usesNonSdkApi() || isPackageWhitelistedForHiddenApis();
         } else {
             return false;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7d8ff4c..5b0e856 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2274,7 +2274,6 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
-     * @hide
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_FACE = "android.hardware.face";
@@ -2282,7 +2281,6 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
-     * @hide
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_IRIS = "android.hardware.iris";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8b058dc..6d49362 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3659,8 +3659,10 @@
             ai.appComponentFactory = buildClassName(ai.packageName, factory, outError);
         }
 
-        ai.usesNonSdkApi = sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false);
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false)) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
+        }
 
         if (outError[0] == null) {
             CharSequence pname;
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index b4955ea..496f34c 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -30,6 +30,12 @@
  * @hide Only for use within the system server.
  */
 public final class DisplayViewport {
+
+    // Viewport constants defined in InputReader.h.
+    public static final int VIEWPORT_INTERNAL = 1;
+    public static final int VIEWPORT_EXTERNAL = 2;
+    public static final int VIEWPORT_VIRTUAL = 3;
+
     // True if this viewport is valid.
     public boolean valid;
 
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 3141be4..ae0855a 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -300,6 +300,23 @@
     public static final String EXTRA_PERMISSION_GRANTED = "permission";
 
     /**
+     * Name of extra added to start systemui.usb.UsbPermissionActivity
+     * containing package name of the app which requests USB permission.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PACKAGE = "android.hardware.usb.extra.PACKAGE";
+
+    /**
+     * Name of extra added to start systemui.usb.UsbPermissionActivity
+     * containing the whether the app which requests USB permission can be set as default handler
+     * for USB device attach event or USB accessory attach event or not.
+     *
+     * @hide
+     */
+    public static final String EXTRA_CAN_BE_DEFAULT = "android.hardware.usb.extra.CAN_BE_DEFAULT";
+
+    /**
      * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 9295bb7..64c934b 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -450,10 +450,12 @@
         @MainThread
         @Override
         public void attachToken(IBinder token) {
-            if (mToken == null) {
-                mToken = token;
-                mWindow.setToken(token);
+            if (mToken != null) {
+                throw new IllegalStateException(
+                        "attachToken() must be called at most once. token=" + token);
             }
+            mToken = token;
+            mWindow.setToken(token);
         }
 
         /**
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 684a8ee..ded3a19 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.LooperProto;
 import android.util.Log;
 import android.util.Printer;
 import android.util.Slog;
@@ -70,6 +69,7 @@
     // sThreadLocal.get() will return null unless you've called prepare().
     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
     private static Looper sMainLooper;  // guarded by Looper.class
+    private static Observer sObserver;
 
     final MessageQueue mQueue;
     final Thread mThread;
@@ -131,6 +131,15 @@
     }
 
     /**
+     * Set the transaction observer for all Loopers in this process.
+     *
+     * @hide
+     */
+    public static void setObserver(@Nullable Observer observer) {
+        sObserver = observer;
+    }
+
+    /**
      * Run the message queue in this thread. Be sure to call
      * {@link #quit()} to end the loop.
      */
@@ -169,6 +178,8 @@
                 logging.println(">>>>> Dispatching to " + msg.target + " " +
                         msg.callback + ": " + msg.what);
             }
+            // Make sure the observer won't change while processing a transaction.
+            final Observer observer = sObserver;
 
             final long traceTag = me.mTraceTag;
             long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
@@ -189,9 +200,21 @@
 
             final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
             final long dispatchEnd;
+            Object token = null;
+            if (observer != null) {
+                token = observer.messageDispatchStarting();
+            }
             try {
                 msg.target.dispatchMessage(msg);
+                if (observer != null) {
+                    observer.messageDispatched(token, msg);
+                }
                 dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
+            } catch (Exception exception) {
+                if (observer != null) {
+                    observer.dispatchingThrewException(token, msg, exception);
+                }
+                throw exception;
             } finally {
                 if (traceTag != 0) {
                     Trace.traceEnd(traceTag);
@@ -397,4 +420,39 @@
         return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
                 + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
     }
+
+    /** {@hide} */
+    public interface Observer {
+        /**
+         * Called right before a message is dispatched.
+         *
+         * <p> The token type is not specified to allow the implementation to specify its own type.
+         *
+         * @return a token used for collecting telemetry when dispatching a single message.
+         *         The token token must be passed back exactly once to either
+         *         {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
+         *         and must not be reused again.
+         *
+         */
+        Object messageDispatchStarting();
+
+        /**
+         * Called when a message was processed by a Handler.
+         *
+         * @param token Token obtained by previously calling
+         *              {@link Observer#messageDispatchStarting} on the same Observer instance.
+         * @param msg The message that was dispatched.
+         */
+        void messageDispatched(Object token, Message msg);
+
+        /**
+         * Called when an exception was thrown while processing a message.
+         *
+         * @param token Token obtained by previously calling
+         *              {@link Observer#messageDispatchStarting} on the same Observer instance.
+         * @param msg The message that was dispatched and caused an exception.
+         * @param exception The exception that was thrown.
+         */
+        void dispatchingThrewException(Object token, Message msg, Exception exception);
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a1cc67d..e007398 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7256,6 +7256,14 @@
         private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Gesture that wakes up the display, showing the ambient version of the status bar.
+         * @hide
+         */
+        public static final String DOZE_WAKE_SCREEN_GESTURE = "doze_wake_screen_gesture";
+
+        private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -8158,6 +8166,7 @@
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
             DOZE_REACH_GESTURE,
+            DOZE_WAKE_SCREEN_GESTURE,
             NFC_PAYMENT_DEFAULT_COMPONENT,
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
             FACE_UNLOCK_KEYGUARD_ENABLED,
@@ -8302,6 +8311,7 @@
             VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR);
+            VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
             VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
                     AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
@@ -13308,6 +13318,36 @@
          * @hide
          */
         public static final String BINDER_CALLS_STATS = "binder_calls_stats";
+
+        /**
+         * Looper stats settings.
+         *
+         * The following strings are supported as keys:
+         * <pre>
+         *     enabled              (boolean)
+         *     sampling_interval    (int)
+         * </pre>
+         *
+         * @hide
+         */
+        public static final String LOOPER_STATS = "looper_stats";
+
+        /**
+         * Default user id to boot into. They map to user ids, for example, 10, 11, 12.
+         *
+         * @hide
+         */
+        public static final String DEFAULT_USER_ID_TO_BOOT_INTO = "default_boot_into_user_id";
+
+        /**
+         * Persistent user id that is last logged in to.
+         *
+         * They map to user ids, for example, 10, 11, 12.
+         *
+         * @hide
+         */
+        public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
+
     }
 
     /**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index febdb83..4bd86a4 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -164,8 +164,7 @@
         int mType;
         int mCurWidth;
         int mCurHeight;
-        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                         | WindowManager.LayoutParams.FLAG_SCALED;
+        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
         int mWindowPrivateFlags =
                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
         int mCurWindowFlags = mWindowFlags;
@@ -776,6 +775,7 @@
                         mDisplay.getDisplayInfo(displayInfo);
                         mLayout.width = Math.max(displayInfo.logicalWidth, myWidth);
                         mLayout.height = Math.max(displayInfo.logicalHeight, myHeight);
+                        mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED;
                     }
 
                     mLayout.format = mFormat;
@@ -785,8 +785,7 @@
                             | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                             | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                            | WindowManager.LayoutParams.FLAG_SCALED;
+                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                     mCurWindowPrivateFlags = mWindowPrivateFlags;
                     mLayout.privateFlags = mWindowPrivateFlags;
 
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index a7f14b6..b5ade2a 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -45,6 +45,7 @@
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
+        DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
     }
 
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 2c9bb66..9640173 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -232,6 +232,16 @@
     };
 
     /**
+     * Determine if a value is a color by comparing {@link type} to {@link #TYPE_FIRST_COLOR_INT}
+     * and {@link #TYPE_LAST_COLOR_INT}.
+     *
+     * @return true if this value is a color
+     */
+    public boolean isColorType() {
+        return (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT);
+    }
+
+    /**
      * Retrieve the base value from a complex data integer.  This uses the 
      * {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of 
      * the data to compute a floating point representation of the number they 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c205af0..bfe1e95 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -391,15 +391,16 @@
     void registerShortcutKey(in long shortcutCode, IShortcutService keySubscriber);
 
     /**
-     * Create an input consumer by name.
+     * Create an input consumer by name and display id.
      */
-    void createInputConsumer(IBinder token, String name, out InputChannel inputChannel);
+    void createInputConsumer(IBinder token, String name, int displayId,
+        out InputChannel inputChannel);
 
     /**
-     * Destroy an input consumer by name.  This method will also dispose the input channels
-     * associated with that InputConsumer.
+     * Destroy an input consumer by name and display id.
+     * This method will also dispose the input channels associated with that InputConsumer.
      */
-    boolean destroyInputConsumer(String name);
+    boolean destroyInputConsumer(String name, int displayId);
 
     /**
      * Return the touch region for the current IME window, or an empty region if there is none.
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index ab8886b..ca14ebe 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -86,10 +86,10 @@
      * needed to identify itself with the service to validate its operations.
      * This token <strong>must not</strong> be passed to applications, since
      * it grants special priviledges that should not be given to applications.
-     * 
-     * <p>Note: to protect yourself from malicious clients, you should only
-     * accept the first token given to you.  Any after that may come from the
-     * client.
+     *
+     * <p>The system guarantees that this method is called back between
+     * {@link InputMethodService#onCreate()} and {@link InputMethodService#onDestroy()}
+     * at most once.
      */
     @MainThread
     public void attachToken(IBinder token);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d3fd0cf..0ecd09f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2333,7 +2333,13 @@
     }
 
     /**
-     * @return The current height of the input method window.
+     * This is kept due to {@link android.annotation.UnsupportedAppUsage}.
+     *
+     * <p>TODO(Bug 113914148): Check if we can remove this.  We have accidentally exposed
+     * WindowManagerInternal#getInputMethodWindowVisibleHeight to app developers and some of them
+     * started relying on it.</p>
+     *
+     * @return Something that is not well-defined.
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0da47fd..1883809 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -545,7 +545,7 @@
             StateListDrawable stateList = (StateListDrawable) mBackground;
 
             // Find the above-anchor view - this one's easy, it should be labeled as such.
-            int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
+            int aboveAnchorStateIndex = stateList.findStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
 
             // Now, for the below-anchor view, look for any other drawable specified in the
             // StateListDrawable which is not for the above-anchor state and use that.
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 8728367..cb282b6 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -75,6 +75,15 @@
         return !TextUtils.isEmpty(reachSensorType());
     }
 
+    public boolean wakeScreenGestureEnabled(int user) {
+        return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user)
+                && wakeScreenGestureAvailable();
+    }
+
+    public boolean wakeScreenGestureAvailable() {
+        return !TextUtils.isEmpty(wakeScreenSensorType());
+    }
+
     public String doubleTapSensorType() {
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
@@ -87,6 +96,10 @@
         return mContext.getResources().getString(R.string.config_dozeReachSensorType);
     }
 
+    public String wakeScreenSensorType() {
+        return mContext.getResources().getString(R.string.config_dozeWakeScreenSensorType);
+    }
+
     public boolean pulseOnLongPressEnabled(int user) {
         return pulseOnLongPressAvailable() && boolSettingDefaultOff(
                 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 4aa30f6..c0c358d 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,16 +16,9 @@
 
 package com.android.internal.os;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
 import android.os.Binder;
-import android.os.OsProtoEnums;
-import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
@@ -37,7 +30,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BinderInternal.CallSession;
-import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
@@ -63,7 +55,6 @@
 
     private static final String TAG = "BinderCallsStats";
     private static final int CALL_SESSIONS_POOL_SIZE = 100;
-    private static final int PERIODIC_SAMPLING_INTERVAL = 10;
     private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
     private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
 
@@ -81,25 +72,7 @@
     private final Random mRandom;
     private long mStartTime = System.currentTimeMillis();
 
-    // State updated by the broadcast receiver below.
-    private boolean mScreenInteractive;
-    private boolean mCharging;
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            switch (intent.getAction()) {
-                case Intent.ACTION_BATTERY_CHANGED:
-                    mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
-                    break;
-                case Intent.ACTION_SCREEN_ON:
-                    mScreenInteractive = true;
-                    break;
-                case Intent.ACTION_SCREEN_OFF:
-                    mScreenInteractive = false;
-                    break;
-            }
-        }
-    };
+    private CachedDeviceState.Readonly mDeviceState;
 
     /** Injector for {@link BinderCallsStats}. */
     public static class Injector {
@@ -112,65 +85,14 @@
         this.mRandom = injector.getRandomGenerator();
     }
 
-    public void systemReady(Context context) {
-        registerBroadcastReceiver(context);
-        setInitialState(queryScreenInteractive(context), queryIsCharging());
-    }
-
-    /**
-     * Listens for screen/battery state changes.
-     */
-    @VisibleForTesting
-    public void registerBroadcastReceiver(Context context) {
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        context.registerReceiver(mBroadcastReceiver, filter);
-    }
-
-    /**
-     * Sets the battery/screen initial state.
-     *
-     * This has to be updated *after* the broadcast receiver is installed.
-     */
-    @VisibleForTesting
-    public void setInitialState(boolean isScreenInteractive, boolean isCharging) {
-        this.mScreenInteractive = isScreenInteractive;
-        this.mCharging = isCharging;
-        // Data collected previously was not accurate since the battery/screen state was not set.
-        reset();
-    }
-
-    private boolean queryIsCharging() {
-        final BatteryManagerInternal batteryManager =
-                LocalServices.getService(BatteryManagerInternal.class);
-        if (batteryManager == null) {
-            Slog.wtf(TAG, "BatteryManager null while starting BinderCallsStatsService");
-            // Default to true to not collect any data.
-            return true;
-        } else {
-            return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
-        }
-    }
-
-    private boolean queryScreenInteractive(Context context) {
-        final PowerManager powerManager = context.getSystemService(PowerManager.class);
-        final boolean screenInteractive;
-        if (powerManager == null) {
-            Slog.wtf(TAG, "PowerManager null while starting BinderCallsStatsService",
-                    new Throwable());
-            return true;
-        } else {
-            return powerManager.isInteractive();
-        }
+    public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+        mDeviceState = deviceState;
     }
 
     @Override
     @Nullable
     public CallSession callStarted(Binder binder, int code) {
-        if (mCharging) {
+        if (mDeviceState == null || mDeviceState.isCharging()) {
             return null;
         }
 
@@ -221,7 +143,7 @@
 
         synchronized (mLock) {
             // This was already checked in #callStart but check again while synchronized.
-            if (mCharging) {
+            if (mDeviceState == null || mDeviceState.isCharging()) {
                 return;
             }
 
@@ -233,7 +155,7 @@
                 uidEntry.recordedCallCount++;
 
                 final CallStat callStat = uidEntry.getOrCreate(
-                        s.binderClass, s.transactionCode, mScreenInteractive);
+                        s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
                 callStat.callCount++;
                 callStat.recordedCallCount++;
                 callStat.cpuTimeMicros += duration;
@@ -252,7 +174,7 @@
                 // Only record the total call count if we already track data for this key.
                 // It helps to keep the memory usage down when sampling is enabled.
                 final CallStat callStat = uidEntry.get(
-                        s.binderClass, s.transactionCode, mScreenInteractive);
+                        s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
                 if (callStat != null) {
                     callStat.callCount++;
                 }
@@ -319,13 +241,13 @@
     public ArrayList<ExportedCallStat> getExportedCallStats() {
         // We do not collect all the data if detailed tracking is off.
         if (!mDetailedTracking) {
-            return new ArrayList<ExportedCallStat>();
+            return new ArrayList<>();
         }
 
         ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
         synchronized (mLock) {
             final int uidEntriesSize = mUidEntries.size();
-            for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++){
+            for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) {
                 final UidEntry entry = mUidEntries.valueAt(entryIdx);
                 for (CallStat stat : entry.getCallStatsList()) {
                     ExportedCallStat exported = new ExportedCallStat();
@@ -387,13 +309,15 @@
         }
     }
 
-    public void dump(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+    /** Writes the collected statistics to the supplied {@link PrintWriter}.*/
+    public void dump(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap, boolean verbose) {
         synchronized (mLock) {
             dumpLocked(pw, appIdToPkgNameMap, verbose);
         }
     }
 
-    private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+    private void dumpLocked(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap,
+            boolean verbose) {
         long totalCallsCount = 0;
         long totalRecordedCallsCount = 0;
         long totalCpuTime = 0;
@@ -450,13 +374,13 @@
         for (UidEntry entry : summaryEntries) {
             String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
             pw.println(String.format("  %10d %3.0f%% %8d %8d %s",
-                        entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
-                        entry.recordedCallCount, entry.callCount, uidStr));
+                    entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
+                    entry.recordedCallCount, entry.callCount, uidStr));
         }
         pw.println();
         pw.println(String.format("  Summary: total_cpu_time=%d, "
-                    + "calls_count=%d, avg_call_cpu_time=%.0f",
-                    totalCpuTime, totalCallsCount, (double)totalCpuTime / totalRecordedCallsCount));
+                        + "calls_count=%d, avg_call_cpu_time=%.0f",
+                totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount));
         pw.println();
 
         pw.println("Exceptions thrown (exception_count, class_name):");
@@ -723,11 +647,6 @@
         return result;
     }
 
-    @VisibleForTesting
-    public BroadcastReceiver getBroadcastReceiver() {
-        return mBroadcastReceiver;
-    }
-
     private static int compareByCpuDesc(
             ExportedCallStat a, ExportedCallStat b) {
         return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
new file mode 100644
index 0000000..8c90682
--- /dev/null
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
+ * the System Server telemetry services.
+ *
+ * @hide
+ */
+public class CachedDeviceState {
+    private volatile boolean mScreenInteractive;
+    private volatile boolean mCharging;
+
+    public CachedDeviceState() {
+        mCharging = true;
+        mScreenInteractive = false;
+    }
+
+    @VisibleForTesting
+    public CachedDeviceState(boolean isCharging, boolean isScreenInteractive) {
+        mCharging = isCharging;
+        mScreenInteractive = isScreenInteractive;
+    }
+
+    public void setScreenInteractive(boolean screenInteractive) {
+        mScreenInteractive = screenInteractive;
+    }
+
+    public void setCharging(boolean charging) {
+        mCharging = charging;
+    }
+
+    public Readonly getReadonlyClient() {
+        return new CachedDeviceState.Readonly();
+    }
+
+    /**
+     * Allows for only a readonly access to the device state.
+     */
+    public class Readonly {
+        public boolean isCharging() {
+            return mCharging;
+        }
+
+        public boolean isScreenInteractive() {
+            return mScreenInteractive;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
new file mode 100644
index 0000000..02a8b22
--- /dev/null
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Collects aggregated telemetry data about Looper message dispatching.
+ *
+ * @hide Only for use within the system server.
+ */
+public class LooperStats implements Looper.Observer {
+    private static final int TOKEN_POOL_SIZE = 50;
+
+    @GuardedBy("mLock")
+    private final SparseArray<Entry> mEntries = new SparseArray<>(512);
+    private final Object mLock = new Object();
+    private final Entry mOverflowEntry = new Entry("OVERFLOW");
+    private final Entry mHashCollisionEntry = new Entry("HASH_COLLISION");
+    private final ConcurrentLinkedQueue<DispatchSession> mSessionPool =
+            new ConcurrentLinkedQueue<>();
+    private final int mEntriesSizeCap;
+    private int mSamplingInterval;
+    private CachedDeviceState.Readonly mDeviceState;
+
+    public LooperStats(int samplingInterval, int entriesSizeCap) {
+        this.mSamplingInterval = samplingInterval;
+        this.mEntriesSizeCap = entriesSizeCap;
+    }
+
+    public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+        mDeviceState = deviceState;
+    }
+
+    @Override
+    public Object messageDispatchStarting() {
+        if (deviceStateAllowsCollection() && shouldCollectDetailedData()) {
+            DispatchSession session = mSessionPool.poll();
+            session = session == null ? new DispatchSession() : session;
+            session.startTimeMicro = getElapsedRealtimeMicro();
+            session.cpuStartMicro = getThreadTimeMicro();
+            return session;
+        }
+
+        return DispatchSession.NOT_SAMPLED;
+    }
+
+    @Override
+    public void messageDispatched(Object token, Message msg) {
+        if (!deviceStateAllowsCollection()) {
+            return;
+        }
+
+        DispatchSession session = (DispatchSession) token;
+        Entry entry = getOrCreateEntry(msg);
+        synchronized (entry) {
+            entry.messageCount++;
+            if (session != DispatchSession.NOT_SAMPLED) {
+                entry.recordedMessageCount++;
+                long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
+                long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
+                entry.totalLatencyMicro += latency;
+                entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
+                entry.cpuUsageMicro += cpuUsage;
+                entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+            }
+        }
+
+        recycleSession(session);
+    }
+
+    @Override
+    public void dispatchingThrewException(Object token, Message msg, Exception exception) {
+        if (!deviceStateAllowsCollection()) {
+            return;
+        }
+
+        DispatchSession session = (DispatchSession) token;
+        Entry entry = getOrCreateEntry(msg);
+        synchronized (entry) {
+            entry.exceptionCount++;
+        }
+        recycleSession(session);
+    }
+
+    private boolean deviceStateAllowsCollection() {
+        // Do not collect data if on charger or the state is not set.
+        return mDeviceState != null && !mDeviceState.isCharging();
+    }
+
+    /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
+    public List<ExportedEntry> getEntries() {
+        final ArrayList<ExportedEntry> entries;
+        synchronized (mLock) {
+            final int size = mEntries.size();
+            entries = new ArrayList<>(size);
+            for (int i = 0; i < size; i++) {
+                Entry entry = mEntries.valueAt(i);
+                synchronized (entry) {
+                    entries.add(new ExportedEntry(entry));
+                }
+            }
+        }
+        // Add the overflow and collision entries only if they have any data.
+        if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) {
+            synchronized (mOverflowEntry) {
+                entries.add(new ExportedEntry(mOverflowEntry));
+            }
+        }
+        if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) {
+            synchronized (mHashCollisionEntry) {
+                entries.add(new ExportedEntry(mHashCollisionEntry));
+            }
+        }
+        return entries;
+    }
+
+    /** Removes all collected data. */
+    public void reset() {
+        synchronized (mLock) {
+            mEntries.clear();
+        }
+        synchronized (mHashCollisionEntry) {
+            mHashCollisionEntry.reset();
+        }
+        synchronized (mOverflowEntry) {
+            mOverflowEntry.reset();
+        }
+    }
+
+    public void setSamplingInterval(int samplingInterval) {
+        mSamplingInterval = samplingInterval;
+    }
+
+    @NonNull
+    private Entry getOrCreateEntry(Message msg) {
+        final boolean isInteractive = mDeviceState.isScreenInteractive();
+        final int id = Entry.idFor(msg, isInteractive);
+        Entry entry;
+        synchronized (mLock) {
+            entry = mEntries.get(id);
+            if (entry == null) {
+                if (mEntries.size() >= mEntriesSizeCap) {
+                    // If over the size cap, track totals under a single entry.
+                    return mOverflowEntry;
+                }
+                entry = new Entry(msg, isInteractive);
+                mEntries.put(id, entry);
+            }
+        }
+
+        if (entry.handler.getClass() != msg.getTarget().getClass()
+                || entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread()
+                || entry.isInteractive != isInteractive) {
+            // If a hash collision happened, track totals under a single entry.
+            return mHashCollisionEntry;
+        }
+        return entry;
+    }
+
+    private void recycleSession(DispatchSession session) {
+        if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) {
+            mSessionPool.add(session);
+        }
+    }
+
+    protected long getThreadTimeMicro() {
+        return SystemClock.currentThreadTimeMicro();
+    }
+
+    protected long getElapsedRealtimeMicro() {
+        return SystemClock.elapsedRealtimeNanos() / 1000;
+    }
+
+    protected boolean shouldCollectDetailedData() {
+        return ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+    }
+
+    private static class DispatchSession {
+        static final DispatchSession NOT_SAMPLED = new DispatchSession();
+        public long startTimeMicro;
+        public long cpuStartMicro;
+    }
+
+    private static class Entry {
+        public final Handler handler;
+        public final String messageName;
+        public final boolean isInteractive;
+        public long messageCount;
+        public long recordedMessageCount;
+        public long exceptionCount;
+        public long totalLatencyMicro;
+        public long maxLatencyMicro;
+        public long cpuUsageMicro;
+        public long maxCpuUsageMicro;
+
+        Entry(Message msg, boolean isInteractive) {
+            this.handler = msg.getTarget();
+            this.messageName = handler.getMessageName(msg);
+            this.isInteractive = isInteractive;
+        }
+
+        Entry(String specialEntryName) {
+            this.messageName = specialEntryName;
+            this.handler = null;
+            this.isInteractive = false;
+        }
+
+        void reset() {
+            messageCount = 0;
+            recordedMessageCount = 0;
+            exceptionCount = 0;
+            totalLatencyMicro = 0;
+            maxLatencyMicro = 0;
+            cpuUsageMicro = 0;
+            maxCpuUsageMicro = 0;
+        }
+
+        static int idFor(Message msg, boolean isInteractive) {
+            int result = 7;
+            result = 31 * result + msg.getTarget().getLooper().getThread().hashCode();
+            result = 31 * result + msg.getTarget().getClass().hashCode();
+            result = 31 * result + (isInteractive ? 1231 : 1237);
+            if (msg.getCallback() != null) {
+                return 31 * result + msg.getCallback().getClass().hashCode();
+            } else {
+                return 31 * result + msg.what;
+            }
+        }
+    }
+
+    /** Aggregated data of Looper message dispatching in the in the current process. */
+    public static class ExportedEntry {
+        public final String handlerClassName;
+        public final String threadName;
+        public final String messageName;
+        public final boolean isInteractive;
+        public final long messageCount;
+        public final long recordedMessageCount;
+        public final long exceptionCount;
+        public final long totalLatencyMicros;
+        public final long maxLatencyMicros;
+        public final long cpuUsageMicros;
+        public final long maxCpuUsageMicros;
+
+        ExportedEntry(Entry entry) {
+            if (entry.handler != null) {
+                this.handlerClassName = entry.handler.getClass().getName();
+                this.threadName = entry.handler.getLooper().getThread().getName();
+            } else {
+                // Overflow/collision entries do not have a handler set.
+                this.handlerClassName = "";
+                this.threadName = "";
+            }
+            this.isInteractive = entry.isInteractive;
+            this.messageName = entry.messageName;
+            this.messageCount = entry.messageCount;
+            this.recordedMessageCount = entry.recordedMessageCount;
+            this.exceptionCount = entry.exceptionCount;
+            this.totalLatencyMicros = entry.totalLatencyMicro;
+            this.maxLatencyMicros = entry.maxLatencyMicro;
+            this.cpuUsageMicros = entry.cpuUsageMicro;
+            this.maxCpuUsageMicros = entry.maxCpuUsageMicro;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 4b004e2..09ccf67 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -79,6 +79,8 @@
     boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
     boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
     void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
+    // This is kept due to @UnsupportedAppUsage.
+    // TODO(Bug 113914148): Consider removing this.
     int getInputMethodWindowVisibleHeight();
     void clearLastInputMethodWindowForTransition(in IBinder token);
 
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index f1bd63b..1257336 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -271,7 +271,7 @@
     optional .android.graphics.RectProto containing_frame = 8 [deprecated=true];
     optional .android.graphics.RectProto parent_frame = 9 [deprecated=true];
     optional .android.graphics.RectProto content_frame = 10 [deprecated=true];
-    optional .android.graphics.RectProto content_insets = 11;
+    optional .android.graphics.RectProto content_insets = 11 [deprecated=true];
     optional .android.graphics.RectProto surface_insets = 12;
     optional WindowStateAnimatorProto animator = 13;
     optional bool animating_exit = 14;
@@ -288,10 +288,10 @@
     optional .android.graphics.RectProto visible_frame = 26 [deprecated=true];
     optional .android.graphics.RectProto decor_frame = 27 [deprecated=true];
     optional .android.graphics.RectProto outset_frame = 28 [deprecated=true];
-    optional .android.graphics.RectProto overscan_insets = 29;
-    optional .android.graphics.RectProto visible_insets = 30;
-    optional .android.graphics.RectProto stable_insets = 31;
-    optional .android.graphics.RectProto outsets = 32;
+    optional .android.graphics.RectProto overscan_insets = 29 [deprecated=true];
+    optional .android.graphics.RectProto visible_insets = 30 [deprecated=true];
+    optional .android.graphics.RectProto stable_insets = 31 [deprecated=true];
+    optional .android.graphics.RectProto outsets = 32 [deprecated=true];
     optional .android.view.DisplayCutoutProto cutout = 33 [deprecated=true];
     optional bool remove_on_exit = 34;
     optional bool destroying = 35;
@@ -380,4 +380,9 @@
     optional .android.graphics.RectProto parent_frame = 8;
     optional .android.graphics.RectProto visible_frame = 9;
     optional .android.view.DisplayCutoutProto cutout = 10;
+    optional .android.graphics.RectProto content_insets = 11;
+    optional .android.graphics.RectProto overscan_insets = 12;
+    optional .android.graphics.RectProto visible_insets = 13;
+    optional .android.graphics.RectProto stable_insets = 14;
+    optional .android.graphics.RectProto outsets = 15;
 }
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 3308128..13f6bce 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -40,7 +40,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=3</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index bb0d4d5..d45b91a 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -38,7 +38,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=3</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 1241a9d..b520d5d 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -41,7 +41,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=2</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml
index 232f149..650aa62 100644
--- a/core/res/res/values-mcc302-mnc610/config.xml
+++ b/core/res/res/values-mcc302-mnc610/config.xml
@@ -28,7 +28,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=2</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml
index 1d2e625..4bb68dc 100644
--- a/core/res/res/values-mcc302-mnc640/config.xml
+++ b/core/res/res/values-mcc302-mnc640/config.xml
@@ -24,7 +24,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=2</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index ef1ecd2..11bfa05 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -43,7 +43,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=2</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cf1320c..235f85b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2116,6 +2116,9 @@
     <!-- Type of the reach sensor. Empty if reach is not supported. -->
     <string name="config_dozeReachSensorType" translatable="false"></string>
 
+    <!-- Type of the wake up sensor. Empty if not supported. -->
+    <string name="config_dozeWakeScreenSensorType" translatable="false"></string>
+
     <!-- Control whether the always on display mode is available. This should only be enabled on
          devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
          states. -->
@@ -2793,7 +2796,7 @@
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
-        <item>SUPL_ES=0</item>
+        <item>SUPL_ES=1</item>
         <item>LPP_PROFILE=0</item>
         <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
         <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 48c263e..92cca72 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3462,4 +3462,5 @@
 
   <java-symbol type="integer" name="db_wal_truncate_size" />
   <java-symbol type="integer" name="config_wakeUpDelayDoze" />
+  <java-symbol type="string" name="config_dozeWakeScreenSensorType" />
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 189a4aa..e84aed1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -192,6 +192,7 @@
                     Settings.Global.DEFAULT_DNS_SERVER,
                     Settings.Global.DEFAULT_INSTALL_LOCATION,
                     Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
+                    Settings.Global.DEFAULT_USER_ID_TO_BOOT_INTO,
                     Settings.Global.DESK_DOCK_SOUND,
                     Settings.Global.DESK_UNDOCK_SOUND,
                     Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
@@ -273,6 +274,7 @@
                     Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
                     Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
                     Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+                    Settings.Global.LAST_ACTIVE_USER_ID,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -280,6 +282,7 @@
                     Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                     Settings.Global.LOCK_SOUND,
+                    Settings.Global.LOOPER_STATS,
                     Settings.Global.LOW_BATTERY_SOUND,
                     Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
                     Settings.Global.LOW_POWER_MODE,
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index ace6b2d..364dcfd 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -18,10 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.content.Intent;
-import android.os.BatteryManager;
 import android.os.Binder;
-import android.os.OsProtoEnums;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -34,7 +31,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -50,6 +46,7 @@
     private static final int TEST_UID = 1;
     private static final int REQUEST_SIZE = 2;
     private static final int REPLY_SIZE = 3;
+    private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true);
 
     @Test
     public void testDetailedOff() {
@@ -388,43 +385,27 @@
     }
 
     @Test
-    public void testDataResetWhenInitialStateSet() {
+    public void testNoDataCollectedBeforeInitialDeviceStateSet() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDeviceState(null);
         bcs.setDetailedTracking(true);
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.time += 10;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
 
-        bcs.setInitialState(true, true);
+        bcs.setDeviceState(mDeviceState.getReadonlyClient());
 
         SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
         assertEquals(0, uidEntries.size());
     }
 
     @Test
-    public void testScreenAndChargerInitialStates() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setDetailedTracking(true);
-        Binder binder = new Binder();
-        bcs.setInitialState(true /** screen iteractive */, false);
-
-        CallSession callSession = bcs.callStarted(binder, 1);
-        bcs.time += 10;
-        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
-        List<BinderCallsStats.CallStat> callStatsList =
-                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
-        assertEquals(true, callStatsList.get(0).screenInteractive);
-    }
-
-    @Test
     public void testNoDataCollectedOnCharger() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
-        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
-                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
-        bcs.getBroadcastReceiver().onReceive(null, intent);
+        mDeviceState.setCharging(true);
+
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -436,7 +417,7 @@
     public void testScreenOff() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
-        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_OFF));
+        mDeviceState.setScreenInteractive(false);
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -453,7 +434,7 @@
     public void testScreenOn() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
-        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+        mDeviceState.setScreenInteractive(true);
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -470,9 +451,8 @@
     public void testOnCharger() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
-        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
-                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
-        bcs.getBroadcastReceiver().onReceive(null, intent);
+        mDeviceState.setCharging(true);
+
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -484,9 +464,8 @@
     public void testOnBattery() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
-        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
-                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
-        bcs.getBroadcastReceiver().onReceive(null, intent);
+        mDeviceState.setCharging(false);
+
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -522,7 +501,6 @@
     public void testGetExportedStatsWhenDetailedTrackingEnabled() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
-        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
 
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
@@ -561,7 +539,7 @@
         assertEquals(0, bcs.getExceptionCounts().size());
     }
 
-    static class TestBinderCallsStats extends BinderCallsStats {
+    class TestBinderCallsStats extends BinderCallsStats {
         int callingUid = TEST_UID;
         long time = 1234;
         long elapsedTime = 0;
@@ -580,6 +558,7 @@
                 }
             });
             setSamplingInterval(1);
+            setDeviceState(mDeviceState.getReadonlyClient());
         }
 
         @Override
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
new file mode 100644
index 0000000..0eb3d06
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Comparator;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public final class LooperStatsTest {
+    private HandlerThread mThreadFirst;
+    private HandlerThread mThreadSecond;
+    private Handler mHandlerFirst;
+    private Handler mHandlerSecond;
+    private Handler mHandlerAnonymous;
+    private CachedDeviceState mDeviceState;
+
+    @Before
+    public void setUp() {
+        // The tests are all single-threaded. HandlerThreads are created to allow creating Handlers
+        // and to test Thread name collection.
+        mThreadFirst = new HandlerThread("TestThread1");
+        mThreadSecond = new HandlerThread("TestThread2");
+        mThreadFirst.start();
+        mThreadSecond.start();
+
+        mHandlerFirst = new TestHandlerFirst(mThreadFirst.getLooper());
+        mHandlerSecond = new TestHandlerSecond(mThreadSecond.getLooper());
+        mHandlerAnonymous = new Handler(mThreadFirst.getLooper()) {
+            /* To create an anonymous subclass. */
+        };
+        mDeviceState = new CachedDeviceState();
+        mDeviceState.setCharging(false);
+        mDeviceState.setScreenInteractive(true);
+    }
+
+    @After
+    public void tearDown() {
+        mThreadFirst.quit();
+        mThreadSecond.quit();
+    }
+
+    @Test
+    public void testSingleMessageDispatched() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+        Object token = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(100);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000));
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(1);
+        LooperStats.ExportedEntry entry = entries.get(0);
+        assertThat(entry.threadName).isEqualTo("TestThread1");
+        assertThat(entry.handlerClassName).isEqualTo(
+                "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+        assertThat(entry.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
+        assertThat(entry.isInteractive).isEqualTo(true);
+        assertThat(entry.messageCount).isEqualTo(1);
+        assertThat(entry.recordedMessageCount).isEqualTo(1);
+        assertThat(entry.exceptionCount).isEqualTo(0);
+        assertThat(entry.totalLatencyMicros).isEqualTo(100);
+        assertThat(entry.maxLatencyMicros).isEqualTo(100);
+        assertThat(entry.cpuUsageMicros).isEqualTo(10);
+        assertThat(entry.maxCpuUsageMicros).isEqualTo(10);
+    }
+
+    @Test
+    public void testThrewException() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+        Object token = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(100);
+        looperStats.tickThreadTime(10);
+        looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7),
+                new ArithmeticException());
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(1);
+        LooperStats.ExportedEntry entry = entries.get(0);
+        assertThat(entry.threadName).isEqualTo("TestThread1");
+        assertThat(entry.handlerClassName).isEqualTo(
+                "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+        assertThat(entry.messageName).isEqualTo("0x7"  /* 7 in hex */);
+        assertThat(entry.isInteractive).isEqualTo(true);
+        assertThat(entry.messageCount).isEqualTo(0);
+        assertThat(entry.recordedMessageCount).isEqualTo(0);
+        assertThat(entry.exceptionCount).isEqualTo(1);
+        assertThat(entry.totalLatencyMicros).isEqualTo(0);
+        assertThat(entry.maxLatencyMicros).isEqualTo(0);
+        assertThat(entry.cpuUsageMicros).isEqualTo(0);
+        assertThat(entry.maxCpuUsageMicros).isEqualTo(0);
+    }
+
+    @Test
+    public void testMultipleMessagesDispatched() {
+        TestableLooperStats looperStats = new TestableLooperStats(2, 100);
+
+        // Contributes to entry2.
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(100);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+        // Contributes to entry2.
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(50);
+        looperStats.tickThreadTime(20);
+        looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1000));
+
+        // Contributes to entry3.
+        Object token3 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(10);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
+        }));
+
+        // Contributes to entry1.
+        Object token4 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(100);
+        looperStats.tickThreadTime(100);
+        looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1));
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(3);
+        entries.sort(Comparator.comparing(e -> e.handlerClassName));
+
+        // Captures data for token4 call.
+        LooperStats.ExportedEntry entry1 = entries.get(0);
+        assertThat(entry1.threadName).isEqualTo("TestThread1");
+        assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
+        assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
+        assertThat(entry1.messageCount).isEqualTo(1);
+        assertThat(entry1.recordedMessageCount).isEqualTo(0);
+        assertThat(entry1.exceptionCount).isEqualTo(0);
+        assertThat(entry1.totalLatencyMicros).isEqualTo(0);
+        assertThat(entry1.maxLatencyMicros).isEqualTo(0);
+        assertThat(entry1.cpuUsageMicros).isEqualTo(0);
+        assertThat(entry1.maxCpuUsageMicros).isEqualTo(0);
+
+        // Captures data for token1 and token2 calls.
+        LooperStats.ExportedEntry entry2 = entries.get(1);
+        assertThat(entry2.threadName).isEqualTo("TestThread1");
+        assertThat(entry2.handlerClassName).isEqualTo(
+                "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+        assertThat(entry2.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
+        assertThat(entry2.messageCount).isEqualTo(2);
+        assertThat(entry2.recordedMessageCount).isEqualTo(1);
+        assertThat(entry2.exceptionCount).isEqualTo(0);
+        assertThat(entry2.totalLatencyMicros).isEqualTo(100);
+        assertThat(entry2.maxLatencyMicros).isEqualTo(100);
+        assertThat(entry2.cpuUsageMicros).isEqualTo(10);
+        assertThat(entry2.maxCpuUsageMicros).isEqualTo(10);
+
+        // Captures data for token3 call.
+        LooperStats.ExportedEntry entry3 = entries.get(2);
+        assertThat(entry3.threadName).isEqualTo("TestThread2");
+        assertThat(entry3.handlerClassName).isEqualTo(
+                "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
+        assertThat(entry3.messageName).startsWith(
+                "com.android.internal.os.-$$Lambda$LooperStatsTest$");
+        assertThat(entry3.messageCount).isEqualTo(1);
+        assertThat(entry3.recordedMessageCount).isEqualTo(1);
+        assertThat(entry3.exceptionCount).isEqualTo(0);
+        assertThat(entry3.totalLatencyMicros).isEqualTo(10);
+        assertThat(entry3.maxLatencyMicros).isEqualTo(10);
+        assertThat(entry3.cpuUsageMicros).isEqualTo(10);
+        assertThat(entry3.maxCpuUsageMicros).isEqualTo(10);
+    }
+
+    @Test
+    public void testDataNotCollectedBeforeDeviceStateSet() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        looperStats.setDeviceState(null);
+
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+                new IllegalArgumentException());
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(0);
+    }
+
+    @Test
+    public void testDataNotCollectedOnCharger() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        mDeviceState.setCharging(true);
+
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+                new IllegalArgumentException());
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(0);
+    }
+
+    @Test
+    public void testScreenStateCollected() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+        mDeviceState.setScreenInteractive(true);
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+                new IllegalArgumentException());
+
+        Object token3 = looperStats.messageDispatchStarting();
+        // If screen state changed during the call, we take the final state into account.
+        mDeviceState.setScreenInteractive(false);
+        looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1000));
+        Object token4 = looperStats.messageDispatchStarting();
+        looperStats.dispatchingThrewException(token4, mHandlerFirst.obtainMessage(1000),
+                new IllegalArgumentException());
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(2);
+        entries.sort(Comparator.comparing(e -> e.isInteractive));
+
+        LooperStats.ExportedEntry entry1 = entries.get(0);
+        assertThat(entry1.isInteractive).isEqualTo(false);
+        assertThat(entry1.messageCount).isEqualTo(1);
+        assertThat(entry1.exceptionCount).isEqualTo(1);
+
+        LooperStats.ExportedEntry entry2 = entries.get(1);
+        assertThat(entry2.isInteractive).isEqualTo(true);
+        assertThat(entry2.messageCount).isEqualTo(1);
+        assertThat(entry2.exceptionCount).isEqualTo(1);
+    }
+
+    @Test
+    public void testMessagesOverSizeCap() {
+        TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */);
+
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(100);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(50);
+        looperStats.tickThreadTime(20);
+        looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1001));
+
+        Object token3 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(10);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1002));
+
+        Object token4 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(10);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(1003));
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(2);
+        entries.sort(Comparator.comparing(e -> e.handlerClassName));
+
+        LooperStats.ExportedEntry entry1 = entries.get(0);
+        assertThat(entry1.threadName).isEqualTo("");
+        assertThat(entry1.handlerClassName).isEqualTo("");
+        assertThat(entry1.messageName).isEqualTo("OVERFLOW");
+        assertThat(entry1.messageCount).isEqualTo(3);
+        assertThat(entry1.recordedMessageCount).isEqualTo(1);
+        assertThat(entry1.exceptionCount).isEqualTo(0);
+        assertThat(entry1.totalLatencyMicros).isEqualTo(10);
+        assertThat(entry1.maxLatencyMicros).isEqualTo(10);
+        assertThat(entry1.cpuUsageMicros).isEqualTo(10);
+        assertThat(entry1.maxCpuUsageMicros).isEqualTo(10);
+
+        LooperStats.ExportedEntry entry2 = entries.get(1);
+        assertThat(entry2.threadName).isEqualTo("TestThread1");
+        assertThat(entry2.handlerClassName).isEqualTo(
+                "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+    }
+
+    @Test
+    public void testInvalidTokensCauseException() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        assertThrows(ClassCastException.class,
+                () -> looperStats.dispatchingThrewException(new Object(),
+                        mHandlerFirst.obtainMessage(),
+                        new ArithmeticException()));
+        assertThrows(ClassCastException.class,
+                () -> looperStats.messageDispatched(new Object(), mHandlerFirst.obtainMessage()));
+        assertThrows(ClassCastException.class,
+                () -> looperStats.messageDispatched(123, mHandlerFirst.obtainMessage()));
+        assertThrows(ClassCastException.class,
+                () -> looperStats.messageDispatched(mHandlerFirst.obtainMessage(),
+                        mHandlerFirst.obtainMessage()));
+
+        assertThat(looperStats.getEntries()).hasSize(0);
+    }
+
+    @Test
+    public void testTracksMultipleHandlerInstancesIfSameClass() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        Handler handlerFirstAnother = new TestHandlerFirst(mHandlerFirst.getLooper());
+
+        Object token1 = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+        Object token2 = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token2, handlerFirstAnother.obtainMessage(1000));
+
+        assertThat(looperStats.getEntries()).hasSize(1);
+        assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2);
+    }
+
+    private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
+        try {
+            r.run();
+            Assert.fail("Expected " + exceptionClass + " to be thrown.");
+        } catch (Exception exception) {
+            assertThat(exception).isInstanceOf(exceptionClass);
+        }
+    }
+
+    private final class TestableLooperStats extends LooperStats {
+        private static final long INITIAL_MICROS = 10001000123L;
+        private int mCount;
+        private long mRealtimeMicros;
+        private long mThreadTimeMicros;
+        private int mSamplingInterval;
+
+        TestableLooperStats(int samplingInterval, int sizeCap) {
+            super(samplingInterval, sizeCap);
+            this.mSamplingInterval = samplingInterval;
+            this.setDeviceState(mDeviceState.getReadonlyClient());
+        }
+
+        void tickRealtime(long micros) {
+            mRealtimeMicros += micros;
+        }
+
+        void tickThreadTime(long micros) {
+            mThreadTimeMicros += micros;
+        }
+
+        @Override
+        protected long getElapsedRealtimeMicro() {
+            return INITIAL_MICROS + mRealtimeMicros;
+        }
+
+        @Override
+        protected long getThreadTimeMicro() {
+            return INITIAL_MICROS + mThreadTimeMicros;
+        }
+
+        @Override
+        protected boolean shouldCollectDetailedData() {
+            return mCount++ % mSamplingInterval == 0;
+        }
+    }
+
+    private static final class TestHandlerFirst extends Handler {
+        TestHandlerFirst(Looper looper) {
+            super(looper);
+        }
+    }
+
+    private static final class TestHandlerSecond extends Handler {
+        TestHandlerSecond(Looper looper) {
+            super(looper);
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9dab536..7fc1787 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -18,6 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
+import android.annotation.Px;
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
@@ -805,25 +806,39 @@
      * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
      *
      * @return true if the underlineText bit is set in the paint's flags.
+     * @see #getUnderlinePosition()
+     * @see #getUnderlineThickness()
+     * @see #setUnderlineText(boolean)
      */
     public final boolean isUnderlineText() {
         return (getFlags() & UNDERLINE_TEXT_FLAG) != 0;
     }
 
     /**
-     * Distance from top of the underline to the baseline. Positive values mean below the baseline.
-     * This method returns where the underline should be drawn independent of if the underlineText
-     * bit is set at the moment.
-     * @hide
+     * Returns the distance from top of the underline to the baseline in pixels.
+     *
+     * The result is positive for positions that are below the baseline.
+     * This method returns where the underline should be drawn independent of if the {@link
+     * #UNDERLINE_TEXT_FLAG} bit is set.
+     *
+     * @return the position of the underline in pixels
+     * @see #isUnderlineText()
+     * @see #getUnderlineThickness()
+     * @see #setUnderlineText(boolean)
      */
-    public float getUnderlinePosition() {
+    public @Px float getUnderlinePosition() {
         return nGetUnderlinePosition(mNativePaint);
     }
 
     /**
-     * @hide
+     * Returns the thickness of the underline in pixels.
+     *
+     * @return the thickness of the underline in pixels
+     * @see #isUnderlineText()
+     * @see #getUnderlinePosition()
+     * @see #setUnderlineText(boolean)
      */
-    public float getUnderlineThickness() {
+    public @Px float getUnderlineThickness() {
         return nGetUnderlineThickness(mNativePaint);
     }
 
@@ -832,6 +847,9 @@
      *
      * @param underlineText true to set the underlineText bit in the paint's
      *                      flags, false to clear it.
+     * @see #isUnderlineText()
+     * @see #getUnderlinePosition()
+     * @see #getUnderlineThickness()
      */
     public void setUnderlineText(boolean underlineText) {
         nSetUnderlineText(mNativePaint, underlineText);
@@ -840,26 +858,40 @@
     /**
      * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
      *
-     * @return true if the strikeThruText bit is set in the paint's flags.
+     * @return true if the {@link #STRIKE_THRU_TEXT_FLAG} bit is set in the paint's flags.
+     * @see #getStrikeThruPosition()
+     * @see #getStrikeThruThickness()
+     * @see #setStrikeThruText(boolean)
      */
     public final boolean isStrikeThruText() {
         return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0;
     }
 
     /**
-     * Distance from top of the strike-through line to the baseline. Negative values mean above the
-     * baseline. This method returns where the strike-through line should be drawn independent of if
-     * the strikeThruText bit is set at the moment.
-     * @hide
+     * Distance from top of the strike-through line to the baseline in pixels.
+     *
+     * The result is negative for positions that are above the baseline.
+     * This method returns where the strike-through line should be drawn independent of if the
+     * {@link #STRIKE_THRU_TEXT_FLAG} bit is set.
+     *
+     * @return the position of the strike-through line in pixels
+     * @see #getStrikeThruThickness()
+     * @see #setStrikeThruText(boolean)
+     * @see #isStrikeThruText()
      */
-    public float getStrikeThruPosition() {
+    public @Px float getStrikeThruPosition() {
         return nGetStrikeThruPosition(mNativePaint);
     }
 
     /**
-     * @hide
+     * Returns the thickness of the strike-through line in pixels.
+     *
+     * @return the position of the strike-through line in pixels
+     * @see #getStrikeThruPosition()
+     * @see #setStrikeThruText(boolean)
+     * @see #isStrikeThruText()
      */
-    public float getStrikeThruThickness() {
+    public @Px float getStrikeThruThickness() {
         return nGetStrikeThruThickness(mNativePaint);
     }
 
@@ -868,6 +900,9 @@
      *
      * @param strikeThruText true to set the strikeThruText bit in the paint's
      *                       flags, false to clear it.
+     * @see #getStrikeThruPosition()
+     * @see #getStrikeThruThickness()
+     * @see #isStrikeThruText()
      */
     public void setStrikeThruText(boolean strikeThruText) {
         nSetStrikeThruText(mNativePaint, strikeThruText);
@@ -1560,22 +1595,25 @@
     }
 
     /**
-     * Return the paint's word-spacing for text. The default value is 0.
+     * Return the paint's extra word-spacing for text.
      *
-     * @return the paint's word-spacing for drawing text.
-     * @hide
+     * The default value is 0.
+     *
+     * @return the paint's extra word-spacing for drawing text in pixels.
+     * @see #setWordSpacing(float)
      */
     public float getWordSpacing() {
         return nGetWordSpacing(mNativePaint);
     }
 
     /**
-     * Set the paint's word-spacing for text. The default value is 0.
-     * The value is in pixels (note the units are not the same as for
-     * letter-spacing).
+     * Set the paint's extra word-spacing for text.
      *
-     * @param wordSpacing set the paint's word-spacing for drawing text.
-     * @hide
+     * Increases the white space width between words with the given amount of pixels.
+     * The default value is 0.
+     *
+     * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels.
+     * @see #getWordSpacing()
      */
     public void setWordSpacing(float wordSpacing) {
         nSetWordSpacing(mNativePaint, wordSpacing);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 8de8f81..2855227 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -280,7 +280,7 @@
      * @see #getStateDrawable(int)
      * @see #getStateSet(int)
      */
-    public int getStateDrawableIndex(int[] stateSet) {
+    public int findStateDrawableIndex(int[] stateSet) {
         return mStateListState.indexOfStateSet(stateSet);
     }
 
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index d985633..e8332a8 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -178,8 +178,8 @@
     struct stat st = {};
     LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont);
     void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
-    std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size));
-    sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release());
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size));
+    sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData));
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
 
     std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index e8bc622..0476222 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1035,7 +1035,7 @@
             return fromGetVolumeControlStream ?
                     AudioSystem.STREAM_SYSTEM : AudioSystem.STREAM_SYSTEM_ENFORCED;
         }
-        if ((aa.getFlags() & FLAG_SCO) == FLAG_SCO) {
+        if ((aa.getAllFlags() & FLAG_SCO) == FLAG_SCO) {
             return fromGetVolumeControlStream ?
                     AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
         }
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 0892f73..967e13f 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -38,7 +38,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal|bottom"
         android:gravity="center_vertical"
-        android:visibility="invisible">
+        android:alpha="0">
 
         <ImageView
             android:id="@+id/next_alarm_icon"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d051f07..26eadb5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -361,7 +361,7 @@
     <dimen name="qs_quick_layout_width">-1px</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
-    <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
+    <dimen name="qs_header_tile_margin_horizontal">4dp</dimen>
     <dimen name="qs_page_indicator_width">16dp</dimen>
     <dimen name="qs_page_indicator_height">8dp</dimen>
     <dimen name="qs_tile_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index 0d25c91..0cde9da 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.system;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 
@@ -146,8 +147,9 @@
         if (mInputEventReceiver == null) {
             final InputChannel inputChannel = new InputChannel();
             try {
-                mWindowManager.destroyInputConsumer(mName);
-                mWindowManager.createInputConsumer(mToken, mName, inputChannel);
+                // TODO(b/113087003): Support Picture-in-picture in multi-display.
+                mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+                mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to create input consumer", e);
             }
@@ -164,7 +166,8 @@
     public void unregisterInputConsumer() {
         if (mInputEventReceiver != null) {
             try {
-                mWindowManager.destroyInputConsumer(mName);
+                // TODO(b/113087003): Support Picture-in-picture in multi-display.
+                mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to destroy input consumer", e);
             }
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 5605b7a..ac8f024 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -39,6 +39,7 @@
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.systemui.util.EmergencyDialerConstants;
 
 /**
  * This class implements a smart emergency button that updates itself based
@@ -48,11 +49,13 @@
  */
 public class EmergencyButton extends Button {
     private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
-            .setAction("com.android.phone.EmergencyDialer.DIAL")
+            .setAction(EmergencyDialerConstants.ACTION_DIAL)
             .setPackage("com.android.phone")
             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                    | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                    | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+            .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+                    EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
 
     private static final String LOG_TAG = "EmergencyButton";
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 51cc4a1..ab30f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int PULSE_REASONS = 7;
+    private static final int REASONS = 8;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -45,6 +45,7 @@
     public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
     public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
     public static final int PULSE_REASON_SENSOR_REACH = 6;
+    public static final int REASON_SENSOR_WAKE_UP = 7;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -74,7 +75,7 @@
     public static void tracePulseStart(int reason) {
         if (!ENABLED) return;
         sPulsing = true;
-        log("pulseStart reason=" + pulseReasonToString(reason));
+        log("pulseStart reason=" + reasonToString(reason));
     }
 
     public static void tracePulseFinish() {
@@ -102,8 +103,8 @@
                 sScreenOnPulsingStats = new SummaryStats();
                 sScreenOnNotPulsingStats = new SummaryStats();
                 sEmergencyCallStats = new SummaryStats();
-                sProxStats = new SummaryStats[PULSE_REASONS][2];
-                for (int i = 0; i < PULSE_REASONS; i++) {
+                sProxStats = new SummaryStats[REASONS][2];
+                for (int i = 0; i < REASONS; i++) {
                     sProxStats[i][0] = new SummaryStats();
                     sProxStats[i][1] = new SummaryStats();
                 }
@@ -176,15 +177,15 @@
     }
 
     public static void traceProximityResult(Context context, boolean near, long millis,
-            int pulseReason) {
+            int reason) {
         if (!ENABLED) return;
         init(context);
-        log("proximityResult reason=" + pulseReasonToString(pulseReason) + " near=" + near
+        log("proximityResult reason=" + reasonToString(reason) + " near=" + near
                 + " millis=" + millis);
-        sProxStats[pulseReason][near ? 0 : 1].append();
+        sProxStats[reason][near ? 0 : 1].append();
     }
 
-    public static String pulseReasonToString(int pulseReason) {
+    public static String reasonToString(int pulseReason) {
         switch (pulseReason) {
             case PULSE_REASON_INTENT: return "intent";
             case PULSE_REASON_NOTIFICATION: return "notification";
@@ -193,6 +194,7 @@
             case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
             case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
             case PULSE_REASON_SENSOR_REACH: return "reach";
+            case REASON_SENSOR_WAKE_UP: return "wakeup";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
     }
@@ -218,8 +220,8 @@
             sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
             sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
             sEmergencyCallStats.dump(pw, "Emergency call");
-            for (int i = 0; i < PULSE_REASONS; i++) {
-                final String reason = pulseReasonToString(i);
+            for (int i = 0; i < REASONS; i++) {
+                final String reason = reasonToString(i);
                 sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
                 sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
             }
@@ -262,10 +264,10 @@
         }
     }
 
-    public static void traceSensor(Context context, int pulseReason) {
+    public static void traceSensor(Context context, int reason) {
         if (!ENABLED) return;
         init(context);
-        log("sensor type=" + pulseReasonToString(pulseReason));
+        log("sensor type=" + reasonToString(reason));
     }
 
     private static class SummaryStats {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 045a98c..f9dfb5d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -63,6 +63,7 @@
     private final AmbientDisplayConfiguration mConfig;
     private final WakeLock mWakeLock;
     private final Consumer<Boolean> mProxCallback;
+    private final Consumer<Boolean> mWakeScreenCallback;
     private final Callback mCallback;
 
     private final Handler mHandler = new Handler();
@@ -70,9 +71,9 @@
 
 
     public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
-            DozeParameters dozeParameters,
-            AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
-            Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
+            DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
+            Callback callback, Consumer<Boolean> proxCallback,
+            Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
         mContext = context;
         mAlarmManager = alarmManager;
         mSensorManager = sensorManager;
@@ -80,6 +81,7 @@
         mConfig = config;
         mWakeLock = wakeLock;
         mProxCallback = proxCallback;
+        mWakeScreenCallback = wakeScreenCallback;
         mResolver = mContext.getContentResolver();
 
         mSensors = new TriggerSensor[] {
@@ -117,6 +119,7 @@
                         DozeLog.PULSE_REASON_SENSOR_REACH,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
+                new WakeScreenSensor(),
         };
 
         mProxSensor = new ProxSensor(policy);
@@ -302,9 +305,9 @@
         final boolean mSettingDefault;
         final boolean mRequiresTouchscreen;
 
-        private boolean mRequested;
-        private boolean mRegistered;
-        private boolean mDisabled;
+        protected boolean mRequested;
+        protected boolean mRegistered;
+        protected boolean mDisabled;
 
         public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
                 boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
@@ -348,7 +351,7 @@
             }
         }
 
-        private boolean enabledBySetting() {
+        protected boolean enabledBySetting() {
             if (TextUtils.isEmpty(mSetting)) {
                 return true;
             }
@@ -401,7 +404,7 @@
             }
         }
 
-        private String triggerEventToString(TriggerEvent event) {
+        protected String triggerEventToString(TriggerEvent event) {
             if (event == null) return null;
             final StringBuilder sb = new StringBuilder("TriggerEvent[")
                     .append(event.timestamp).append(',')
@@ -415,6 +418,28 @@
         }
     }
 
+    private class WakeScreenSensor extends TriggerSensor {
+
+        WakeScreenSensor() {
+            super(findSensorWithType(mConfig.wakeScreenSensorType()),
+                    Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
+                    DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
+                    false /* requiresTouchscreen */);
+        }
+
+        @Override
+        @AnyThread
+        public void onTrigger(TriggerEvent event) {
+            DozeLog.traceSensor(mContext, mPulseReason);
+            mHandler.post(mWakeLock.wrap(() -> {
+                if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+                mRegistered = false;
+                mWakeScreenCallback.accept(event.values[0] > 0);
+                updateListener();  // reregister, this sensor only fires once
+            }));
+        }
+    }
+
     public interface Callback {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 73cbd7d..1589969 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
-                config, wakeLock, this::onSensor, this::onProximityFar,
+                config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
                 dozeParameters.getPolicy());
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
     }
@@ -103,7 +103,7 @@
 
     private void proximityCheckThenCall(IntConsumer callback,
             boolean alreadyPerformedProxCheck,
-            int pulseReason) {
+            int reason) {
         Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
         if (alreadyPerformedProxCheck) {
             callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
@@ -116,7 +116,7 @@
                 public void onProximityResult(int result) {
                     final long end = SystemClock.uptimeMillis();
                     DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
-                            end - start, pulseReason);
+                            end - start, reason);
                     callback.accept(result);
                 }
             }.check();
@@ -182,6 +182,28 @@
         }
     }
 
+    private void onWakeScreen(boolean wake) {
+        DozeMachine.State state = mMachine.getState();
+        boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
+        boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+
+        if (wake) {
+            proximityCheckThenCall((result) -> {
+                if (result == ProximityCheck.RESULT_NEAR) {
+                    // In pocket, drop event.
+                    return;
+                }
+                if (pausing || paused) {
+                    mMachine.requestState(DozeMachine.State.DOZE_AOD);
+                }
+            }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+        } else {
+            if (!pausing && !paused) {
+                mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+            }
+        }
+    }
+
     @Override
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         switch (newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 06d4434..cc1b9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -89,6 +89,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.util.EmergencyDialerConstants;
 import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
 
 import java.util.ArrayList;
@@ -449,9 +450,6 @@
     }
 
     private class EmergencyDialerAction extends SinglePressAction {
-        private static final String ACTION_EMERGENCY_DIALER_DIAL =
-                "com.android.phone.EmergencyDialer.DIAL";
-
         private EmergencyDialerAction() {
             super(R.drawable.ic_faster_emergency,
                     R.string.global_action_emergency);
@@ -460,8 +458,10 @@
         @Override
         public void onPress() {
             MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
-            Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
+            Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+                    EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ff5ac76..b988c55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -547,38 +547,23 @@
 
     /**
      * Fades in the updated status text. Note that if there's already a status showing, this will
-     * immediately hide it and fade in the updated status.
+     * immediately fade it out and fade in the updated status.
      */
     private void showStatus() {
         mStatusContainer.setAlpha(0f);
-        mStatusContainer.setVisibility(View.VISIBLE);
 
-        // Animate the alarm back in. Make sure to clear the animator listener for the animation!
         mStatusContainer.animate()
                 .alpha(1f)
                 .setDuration(FADE_ANIMATION_DURATION_MS)
-                .setListener(null)
                 .start();
     }
 
-    /** Fades out and hides the status text. */
+    /** Fades out the status text. */
     private void hideStatusText() {
-        if (mStatusContainer.getVisibility() == View.VISIBLE) {
-            mStatusContainer.animate()
-                    .alpha(0f)
-                    .setListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            if (DEBUG) Log.d(TAG, "hideAlarmText: Hid alarm text");
-
-                            // Reset the alpha regardless of how the animation ends for the next
-                            // time we show this view/want to animate it.
-                            mStatusContainer.setVisibility(View.INVISIBLE);
-                            mStatusContainer.setAlpha(1f);
-                        }
-                    })
-                    .start();
-        }
+        mStatusContainer.animate()
+                .alpha(0f)
+                .setDuration(FADE_ANIMATION_DURATION_MS)
+                .start();
     }
 
     public void updateEverything() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index afd64f3..0d3ba77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -45,7 +45,7 @@
         public void onDisplayBlanked() {
             if (DEBUG) {
                 Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
-                        + DozeLog.pulseReasonToString(mPulseReason));
+                        + DozeLog.reasonToString(mPulseReason));
             }
             if (!mDozing) {
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 238407a..a46f018 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,17 +16,13 @@
 
 package com.android.systemui.usb;
 
-import android.annotation.NonNull;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.XmlResourceParser;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
@@ -45,13 +41,8 @@
 
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
-import com.android.internal.util.XmlUtils;
-import android.hardware.usb.AccessoryFilter;
-import android.hardware.usb.DeviceFilter;
 import com.android.systemui.R;
 
-import org.xmlpull.v1.XmlPullParser;
-
 public class UsbPermissionActivity extends AlertActivity
         implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
 
@@ -71,12 +62,13 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-       Intent intent = getIntent();
+        Intent intent = getIntent();
         mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
         mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
         mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
         mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-        mPackageName = intent.getStringExtra("package");
+        mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
+        boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false);
 
         PackageManager packageManager = getPackageManager();
         ApplicationInfo aInfo;
@@ -105,121 +97,27 @@
         ap.mPositiveButtonListener = this;
         ap.mNegativeButtonListener = this;
 
-        try {
-            PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
-                    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
-
-            if ((mDevice != null && canBeDefault(mDevice, packageInfo))
-                    || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
-                // add "open when" checkbox
-                LayoutInflater inflater = (LayoutInflater) getSystemService(
-                        Context.LAYOUT_INFLATER_SERVICE);
-                ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
-                mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
-                if (mDevice == null) {
-                    mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
-                            mAccessory.getDescription()));
-                } else {
-                    mAlwaysUse.setText(getString(R.string.always_use_device, appName,
-                            mDevice.getProductName()));
-                }
-                mAlwaysUse.setOnCheckedChangeListener(this);
-
-                mClearDefaultHint = (TextView)ap.mView.findViewById(
-                        com.android.internal.R.id.clearDefaultHint);
-                mClearDefaultHint.setVisibility(View.GONE);
+        if (canBeDefault && (mDevice != null || mAccessory != null)) {
+            // add "open when" checkbox
+            LayoutInflater inflater = (LayoutInflater) getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+            mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+            if (mDevice == null) {
+                mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+                        mAccessory.getDescription()));
+            } else {
+                mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+                        mDevice.getProductName()));
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            // ignore
+            mAlwaysUse.setOnCheckedChangeListener(this);
+
+            mClearDefaultHint = (TextView)ap.mView.findViewById(
+                    com.android.internal.R.id.clearDefaultHint);
+            mClearDefaultHint.setVisibility(View.GONE);
         }
 
         setupAlert();
-
-    }
-
-    /**
-     * Can the app be the default for the USB device. I.e. can the app be launched by default if
-     * the device is plugged in.
-     *
-     * @param device The device the app would be default for
-     * @param packageInfo The package info of the app
-     *
-     * @return {@code true} iff the app can be default
-     */
-    private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
-        ActivityInfo[] activities = packageInfo.activities;
-        if (activities != null) {
-            int numActivities = activities.length;
-            for (int i = 0; i < numActivities; i++) {
-                ActivityInfo activityInfo = activities[i];
-
-                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
-                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
-                    if (parser == null) {
-                        continue;
-                    }
-
-                    XmlUtils.nextElement(parser);
-                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        if ("usb-device".equals(parser.getName())) {
-                            DeviceFilter filter = DeviceFilter.read(parser);
-                            if (filter.matches(device)) {
-                                return true;
-                            }
-                        }
-
-                        XmlUtils.nextElement(parser);
-                    }
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
-     * the accessory is plugged in.
-     *
-     * @param accessory The accessory the app would be default for
-     * @param packageInfo The package info of the app
-     *
-     * @return {@code true} iff the app can be default
-     */
-    private boolean canBeDefault(@NonNull UsbAccessory accessory,
-            @NonNull PackageInfo packageInfo) {
-        ActivityInfo[] activities = packageInfo.activities;
-        if (activities != null) {
-            int numActivities = activities.length;
-            for (int i = 0; i < numActivities; i++) {
-                ActivityInfo activityInfo = activities[i];
-
-                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
-                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
-                    if (parser == null) {
-                        continue;
-                    }
-
-                    XmlUtils.nextElement(parser);
-                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        if ("usb-accessory".equals(parser.getName())) {
-                            AccessoryFilter filter = AccessoryFilter.read(parser);
-                            if (filter.matches(accessory)) {
-                                return true;
-                            }
-                        }
-
-                        XmlUtils.nextElement(parser);
-                    }
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
-                }
-            }
-        }
-
-        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
new file mode 100644
index 0000000..d101ccb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+/**
+ * Constants defined and used in emergency dialer.
+ * Please keep these constants being consistent with those in com.android.phone.EmergencyDialer.
+ */
+public class EmergencyDialerConstants {
+    // Intent action for emergency dialer activity.
+    public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+    /**
+     * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+     * the emergency dialer.
+     */
+    public static final String EXTRA_ENTRY_TYPE =
+            "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
+    // Indicating the entrance to emergency dialer
+    public static final int ENTRY_TYPE_UNKNOWN = 0;
+    public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+    public static final int ENTRY_TYPE_POWER_MENU = 2;
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b668623..7edcdcb 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6508,6 +6508,10 @@
     // OS: Q
     ACTION_EMERGENCY_DIALER_FROM_POWER_MENU = 1569;
 
+    // OPEN: Settings > System > Input & Gesture > Wake screen
+    // OS: Q
+    SETTINGS_GESTURE_WAKE_SCREEN = 1570;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 50f15ca0..a85b69b 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -399,6 +399,12 @@
 
     private void update(android.hardware.health.V2_0.HealthInfo info) {
         traceBegin("HealthInfoUpdate");
+
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
+                info.legacy.batteryChargeCounter);
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
+                info.legacy.batteryCurrent);
+
         synchronized (mLock) {
             if (!mUpdatesStopped) {
                 mHealthInfo = info.legacy;
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 9a7c345..15673a7 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -33,7 +33,7 @@
 
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats;
-import com.android.internal.os.BinderInternal;
+import com.android.internal.os.CachedDeviceState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -41,7 +41,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 
 public class BinderCallsStatsService extends Binder {
 
@@ -156,8 +155,10 @@
         @Override
         public void onBootPhase(int phase) {
             if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+                CachedDeviceState.Readonly deviceState = getLocalService(
+                        CachedDeviceState.Readonly.class);
                 mService.systemReady(getContext());
-                mBinderCallsStats.systemReady(getContext());
+                mBinderCallsStats.setDeviceState(deviceState);
             }
         }
     }
diff --git a/services/core/java/com/android/server/CachedDeviceStateService.java b/services/core/java/com/android/server/CachedDeviceStateService.java
new file mode 100644
index 0000000..38269d3
--- /dev/null
+++ b/services/core/java/com/android/server/CachedDeviceStateService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
+import android.util.Slog;
+
+import com.android.internal.os.CachedDeviceState;
+
+/**
+ * Tracks changes to the device state (e.g. charging/on battery, screen on/off) to share it with
+ * the System Server telemetry services.
+ *
+ * @hide Only for use within the system server.
+ */
+public class CachedDeviceStateService extends SystemService {
+    private static final String TAG = "CachedDeviceStateService";
+    private final CachedDeviceState mDeviceState = new CachedDeviceState();
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_BATTERY_CHANGED:
+                    mDeviceState.setCharging(
+                            intent.getIntExtra(BatteryManager.EXTRA_PLUGGED,
+                                    OsProtoEnums.BATTERY_PLUGGED_NONE)
+                                    != OsProtoEnums.BATTERY_PLUGGED_NONE);
+                    break;
+                case Intent.ACTION_SCREEN_ON:
+                    mDeviceState.setScreenInteractive(true);
+                    break;
+                case Intent.ACTION_SCREEN_OFF:
+                    mDeviceState.setScreenInteractive(false);
+                    break;
+            }
+        }
+    };
+
+    public CachedDeviceStateService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishLocalService(CachedDeviceState.Readonly.class, mDeviceState.getReadonlyClient());
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+            filter.addAction(Intent.ACTION_SCREEN_ON);
+            filter.addAction(Intent.ACTION_SCREEN_OFF);
+            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+            getContext().registerReceiver(mBroadcastReceiver, filter);
+            mDeviceState.setCharging(queryIsCharging());
+            mDeviceState.setScreenInteractive(queryScreenInteractive(getContext()));
+        }
+    }
+
+    private boolean queryIsCharging() {
+        final BatteryManagerInternal batteryManager =
+                LocalServices.getService(BatteryManagerInternal.class);
+        if (batteryManager == null) {
+            Slog.wtf(TAG, "BatteryManager null while starting CachedDeviceStateService");
+            // Default to true to not collect any data.
+            return true;
+        } else {
+            return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
+        }
+    }
+
+    private boolean queryScreenInteractive(Context context) {
+        final PowerManager powerManager = context.getSystemService(PowerManager.class);
+        if (powerManager == null) {
+            Slog.wtf(TAG, "PowerManager null while starting CachedDeviceStateService");
+            return false;
+        } else {
+            return powerManager.isInteractive();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4d3468e..617e803 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3145,6 +3145,14 @@
         return;
     }
 
+    /**
+     * This is kept due to {@link android.annotation.UnsupportedAppUsage} in
+     * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in
+     * {@link InputMethodService#onCreate()}.
+     *
+     * <p>TODO(Bug 113914148): Check if we can remove this.</p>
+     * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()}
+     */
     @Override
     public int getInputMethodWindowVisibleHeight() {
         return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
@@ -4386,12 +4394,9 @@
     private static final class LocalServiceImpl extends InputMethodManagerInternal {
         @NonNull
         private final InputMethodManagerService mService;
-        @NonNull
-        private final Handler mHandler;
 
         LocalServiceImpl(@NonNull InputMethodManagerService service) {
             mService = service;
-            mHandler = service.mHandler;
         }
 
         @Override
@@ -4411,19 +4416,19 @@
         @Override
         public void setInteractive(boolean interactive) {
             // Do everything in handler so as not to block the caller.
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
-                    interactive ? 1 : 0, 0));
+            mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
+                    .sendToTarget();
         }
 
         @Override
         public void hideCurrentInputMethod() {
-            mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
-            mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
+            mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
+            mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
         }
 
         @Override
         public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName));
+            mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
         }
     }
 
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
new file mode 100644
index 0000000..23b30cc
--- /dev/null
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public class LooperStatsService extends Binder {
+    private static final String TAG = "LooperStatsService";
+    private static final String LOOPER_STATS_SERVICE_NAME = "looper_stats";
+    private static final String SETTINGS_ENABLED_KEY = "enabled";
+    private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+    private static final String DEBUG_SYS_LOOPER_STATS_ENABLED =
+            "debug.sys.looper_stats_enabled";
+    private static final int DEFAULT_SAMPLING_INTERVAL = 100;
+    private static final int DEFAULT_ENTRIES_SIZE_CAP = 2000;
+    private static final boolean DEFAULT_ENABLED = false;
+
+    private final Context mContext;
+    private final LooperStats mStats;
+    private boolean mEnabled = false;
+
+    private LooperStatsService(Context context, LooperStats stats) {
+        this.mContext = context;
+        this.mStats = stats;
+    }
+
+    private void initFromSettings() {
+        final KeyValueListParser parser = new KeyValueListParser(',');
+
+        try {
+            parser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                    Settings.Global.LOOPER_STATS));
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Bad looper_stats settings", e);
+        }
+
+        setSamplingInterval(
+                parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, DEFAULT_SAMPLING_INTERVAL));
+        // Manually specified value takes precedence over Settings.
+        setEnabled(SystemProperties.getBoolean(
+                DEBUG_SYS_LOOPER_STATS_ENABLED,
+                parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED)));
+    }
+
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+        (new LooperShellCommand()).exec(this, in, out, err, args, callback, resultReceiver);
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        List<LooperStats.ExportedEntry> entries = mStats.getEntries();
+        entries.sort(Comparator
+                .comparing((LooperStats.ExportedEntry entry) -> entry.threadName)
+                .thenComparing(entry -> entry.handlerClassName)
+                .thenComparing(entry -> entry.messageName));
+        String header = String.join(",", Arrays.asList(
+                "thread_name",
+                "handler_class",
+                "message_name",
+                "is_interactive",
+                "message_count",
+                "recorded_message_count",
+                "total_latency_micros",
+                "max_latency_micros",
+                "total_cpu_micros",
+                "max_cpu_micros",
+                "exception_count"));
+        pw.println(header);
+        for (LooperStats.ExportedEntry entry : entries) {
+            pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName,
+                    entry.handlerClassName, entry.messageName, entry.isInteractive,
+                    entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros,
+                    entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros,
+                    entry.exceptionCount);
+        }
+    }
+
+    private void setEnabled(boolean enabled) {
+        if (mEnabled != enabled) {
+            mEnabled = enabled;
+            mStats.reset();
+            Looper.setObserver(enabled ? mStats : null);
+        }
+    }
+
+    private void setSamplingInterval(int samplingInterval) {
+        mStats.setSamplingInterval(samplingInterval);
+    }
+
+    /**
+     * Manages the lifecycle of LooperStatsService within System Server.
+     */
+    public static class Lifecycle extends SystemService {
+        private final SettingsObserver mSettingsObserver;
+        private final LooperStatsService mService;
+        private final LooperStats mStats;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mStats = new LooperStats(DEFAULT_SAMPLING_INTERVAL, DEFAULT_ENTRIES_SIZE_CAP);
+            mService = new LooperStatsService(getContext(), mStats);
+            mSettingsObserver = new SettingsObserver(mService);
+        }
+
+        @Override
+        public void onStart() {
+            publishLocalService(LooperStats.class, mStats);
+            // TODO: publish LooperStatsService as a binder service when the SE Policy is changed.
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+                mService.initFromSettings();
+                Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LOOPER_STATS);
+                getContext().getContentResolver().registerContentObserver(
+                        settingsUri, false, mSettingsObserver, UserHandle.USER_SYSTEM);
+                mStats.setDeviceState(getLocalService(CachedDeviceState.Readonly.class));
+            }
+        }
+    }
+
+    private static class SettingsObserver extends ContentObserver {
+        private final LooperStatsService mService;
+
+        SettingsObserver(LooperStatsService service) {
+            super(BackgroundThread.getHandler());
+            mService = service;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            mService.initFromSettings();
+        }
+    }
+
+    private class LooperShellCommand extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            if ("enable".equals(cmd)) {
+                setEnabled(true);
+                return 0;
+            } else if ("disable".equals(cmd)) {
+                setEnabled(false);
+                return 0;
+            } else if ("reset".equals(cmd)) {
+                mStats.reset();
+                return 0;
+            } else {
+                return handleDefaultCommands(cmd);
+            }
+        }
+
+        @Override
+        public void onHelp() {
+            final PrintWriter pw = getOutPrintWriter();
+            pw.println(LOOPER_STATS_SERVICE_NAME + " commands:");
+            pw.println("  enable: Enable collecting stats");
+            pw.println("  disable: Disable collecting stats");
+            pw.println("  reset: Reset stats");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b91a449..e6ff0d8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17061,7 +17061,7 @@
             activeInstr.mUiAutomationConnection = uiAutomationConnection;
             activeInstr.mResultClass = className;
 
-            boolean disableHiddenApiChecks = ai.usesNonSdkApi
+            boolean disableHiddenApiChecks = ai.usesNonSdkApi()
                     || (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
             if (disableHiddenApiChecks) {
                 enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9df9ba6..a050aa9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,26 +16,10 @@
 
 package com.android.server.input;
 
+import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
+
 import android.annotation.NonNull;
-import android.os.LocaleList;
-import android.os.ShellCallback;
-import android.util.Log;
-import android.view.Display;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.R;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-import com.android.server.DisplayThread;
-import com.android.server.LocalServices;
-import com.android.server.Watchdog;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import android.Manifest;
 import android.app.IInputForwarder;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -50,8 +34,8 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
@@ -61,10 +45,10 @@
 import android.hardware.display.DisplayViewport;
 import android.hardware.input.IInputDevicesChangedListener;
 import android.hardware.input.IInputManager;
+import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
-import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.TouchCalibration;
 import android.os.Binder;
@@ -72,6 +56,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
@@ -81,6 +66,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -97,6 +83,23 @@
 import android.view.ViewConfiguration;
 import android.widget.Toast;
 
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
+import com.android.server.Watchdog;
+import com.android.server.policy.WindowManagerPolicy;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import org.xmlpull.v1.XmlPullParser;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -113,9 +116,6 @@
 import java.util.Locale;
 import java.util.Objects;
 
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
 /*
  * Wraps the C++ InputManager and provides its callbacks.
  */
@@ -212,7 +212,8 @@
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
             int policyFlags);
     private static native void nativeToggleCapsLock(long ptr, int deviceId);
-    private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
+    private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles,
+            int displayId);
     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
     private static native void nativeSetFocusedApplication(long ptr,
@@ -292,11 +293,6 @@
     /** Switch code: Camera lens cover. When set the lens is covered. */
     public static final int SW_CAMERA_LENS_COVER = 0x09;
 
-    // Viewport constants defined in InputReader.h.
-    public static final int VIEWPORT_DEFAULT = 1;
-    public static final int VIEWPORT_EXTERNAL = 2;
-    public static final int VIEWPORT_VIRTUAL = 3;
-
     public static final int SW_LID_BIT = 1 << SW_LID;
     public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
     public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
@@ -362,7 +358,7 @@
         updateAccessibilityLargePointerFromSettings();
     }
 
-    // TODO(BT) Pass in paramter for bluetooth system
+    // TODO(BT) Pass in parameter for bluetooth system
     public void systemRunning() {
         if (DEBUG) {
             Slog.d(TAG, "System ready.");
@@ -417,7 +413,7 @@
             DisplayViewport externalTouchViewport,
             List<DisplayViewport> virtualTouchViewports) {
         if (defaultViewport.valid) {
-            setDisplayViewport(VIEWPORT_DEFAULT, defaultViewport);
+            setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport);
         }
 
         if (externalTouchViewport.valid) {
@@ -1467,7 +1463,7 @@
     }
 
     public void setInputWindows(InputWindowHandle[] windowHandles,
-            InputWindowHandle focusedWindowHandle) {
+            InputWindowHandle focusedWindowHandle, int displayId) {
         final IWindow newFocusedWindow =
             focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
         if (mFocusedWindow != newFocusedWindow) {
@@ -1476,7 +1472,7 @@
                 setPointerCapture(false);
             }
         }
-        nativeSetInputWindows(mPtr, windowHandles);
+        nativeSetInputWindows(mPtr, windowHandles, displayId);
     }
 
     public void setFocusedApplication(InputApplicationHandle application) {
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 720eaaa5..bb29bf8 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -17,8 +17,8 @@
 package com.android.server.input;
 
 import android.graphics.Region;
-import android.view.InputChannel;
 import android.view.IWindow;
+import android.view.InputChannel;
 
 /**
  * Functions as a handle for a window that can receive input.
@@ -106,7 +106,7 @@
 
     @Override
     public String toString() {
-        return new StringBuilder(name)
+        return new StringBuilder(name != null ? name : "")
                 .append(", layer=").append(layer)
                 .append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
                         .append(frameRight).append(",").append(frameBottom).append("]")
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index a54811b..31cf9e3 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -69,7 +69,6 @@
 import android.provider.Settings;
 import android.provider.Telephony.Carriers;
 import android.telephony.CarrierConfigManager;
-import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
@@ -1577,6 +1576,8 @@
             mSingleShot = false;
             native_stop();
             mLastFixTime = 0;
+            // native_stop() may reset the position mode in hardware.
+            mLastPositionMode = null;
 
             // reset SV count to zero
             updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 41c0be6..b922e40a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -70,6 +70,7 @@
 import com.android.internal.os.KernelUidCpuTimeReader;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.LooperStats;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.util.DumpUtils;
 import com.android.server.BinderCallsStatsService;
@@ -938,6 +939,29 @@
         }
     }
 
+    private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+        LooperStats looperStats = LocalServices.getService(LooperStats.class);
+        if (looperStats == null) {
+            return;
+        }
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        for (LooperStats.ExportedEntry entry : entries) {
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */);
+            e.writeLong(0); // uid collection not implemented yet
+            e.writeString(entry.handlerClassName);
+            e.writeString(entry.threadName);
+            e.writeString(entry.messageName);
+            e.writeLong(entry.messageCount);
+            e.writeLong(entry.exceptionCount);
+            e.writeLong(entry.recordedMessageCount);
+            e.writeLong(entry.totalLatencyMicros);
+            e.writeLong(entry.cpuUsageMicros);
+            pulledData.add(e);
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -1028,6 +1052,10 @@
                 pullBinderCallsStatsExceptions(tagId, ret);
                 break;
             }
+            case StatsLog.LOOPER_STATS: {
+                pullLooperStats(tagId, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b775918..6da9f10 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -32,29 +32,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
+import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
 import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
 import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
@@ -78,6 +60,23 @@
 import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
 import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
 import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 
 import android.annotation.CallSuper;
@@ -1845,8 +1844,8 @@
             surfaceInsets = win.getAttrs().surfaceInsets;
             // XXX(b/72757033): These are insets relative to the window frame, but we're really
             // interested in the insets relative to the frame we chose in the if-blocks above.
-            insets.set(win.mContentInsets);
-            stableInsets.set(win.mStableInsets);
+            win.getContentInsets(insets);
+            win.getStableInsets(stableInsets);
         }
 
         if (mLaunchTaskBehind) {
@@ -2099,7 +2098,7 @@
         WindowState win = findMainWindow();
         Rect appRect = win != null ? win.getContentFrameLw() :
                 new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
-        Rect insets = win != null ? win.mContentInsets : null;
+        final Rect insets = win != null ? win.getContentInsets() : null;
         final Configuration displayConfig = mDisplayContent.getConfiguration();
         return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(
                 appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 32fa9bf..d3e534c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -814,9 +814,7 @@
         // {@link DisplayContent} ready for use.
         mDisplayReady = true;
 
-        // TODO(b/112081256): Use independent InputMonitor.
-        mInputMonitor = isDefaultDisplay ? new InputMonitor(service, mDisplayId)
-                : mService.getDefaultDisplayContentLocked().mInputMonitor;
+        mInputMonitor = new InputMonitor(service, mDisplayId);
     }
 
     boolean isReady() {
@@ -1426,7 +1424,7 @@
         config.densityDpi = displayInfo.logicalDensityDpi;
 
         config.colorMode =
-                (displayInfo.isHdr()
+                ((displayInfo.isHdr() && mService.hasHdrSupport())
                         ? Configuration.COLOR_MODE_HDR_YES
                         : Configuration.COLOR_MODE_HDR_NO)
                         | (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport()
@@ -2185,6 +2183,7 @@
             mRemovingDisplay = false;
         }
 
+        mInputMonitor.onRemoved();
         mService.onDisplayRemoved(mDisplayId);
     }
 
@@ -3009,7 +3008,7 @@
             if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
                 w.mWinAnimator.mDrawState = DRAW_PENDING;
                 // Force add to mResizingWindows.
-                w.mLastContentInsets.set(-1, -1, -1, -1);
+                w.resetLastContentInsets();
                 mService.mWaitingForDrawn.add(w);
             }
         }, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 6a08f4d..585a4f5 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -20,9 +20,9 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.view.Display;
 import android.view.InputChannel;
 import android.view.WindowManager;
+
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.input.InputWindowHandle;
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c4beb55..3309798 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -367,6 +367,13 @@
         }
     }
 
+    void onRemoved() {
+        // If DisplayContent removed, we need find a way to remove window handles of this display
+        // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
+        mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+                mDisplayId);
+    }
+
     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
         InputConsumerImpl navInputConsumer;
         InputConsumerImpl pipInputConsumer;
@@ -399,8 +406,7 @@
             this.inDrag = inDrag;
             wallpaperController = mService.mRoot.mWallpaperController;
 
-            // TODO(b/112081256): Use independent InputMonitor for each display.
-            mService.mRoot/*.getDisplayContent(mDisplayId)*/.forAllWindows(this,
+            mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
                     true /* traverseTopToBottom */);
             if (mAddWallpaperInputConsumerHandle) {
                 // No visible wallpaper found, add the wallpaper input consumer at the end.
@@ -408,8 +414,8 @@
             }
 
             // Send windows to native code.
-            // TODO: Update Input windows and focus by display?
-            mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
+            mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+                    mDisplayId);
 
             clearInputWindowHandlesLw();
 
@@ -429,7 +435,8 @@
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
             final int type = w.mAttrs.type;
-            final boolean hasFocus = w == mInputFocus;
+            // TODO(b/111361570): multi-display focus, one focus window per display.
+            final boolean hasFocus = w.isFocused();
             final boolean isVisible = w.isVisibleLw();
 
             if (mAddRecentsAnimationInputConsumerHandle) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 4dbd858..1eae567 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -373,7 +373,7 @@
                             : null;
             final Rect contentInsets;
             if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) {
-                contentInsets = mTargetAppToken.findMainWindow().mContentInsets;
+                contentInsets = mTargetAppToken.findMainWindow().getContentInsets();
             } else {
                 // If the window for the activity had not yet been created, use the display insets.
                 mService.getStableInsets(mDisplayId, mTmpRect);
@@ -583,7 +583,8 @@
             if (mainWindow == null) {
                 return null;
             }
-            final Rect insets = new Rect(mainWindow.mContentInsets);
+            final Rect insets = new Rect();
+            mainWindow.getContentInsets(insets);
             InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
                     !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 67ef471..00422e3 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -305,7 +305,8 @@
                     || mCapturedLeash == null) {
                 return null;
             }
-            final Rect insets = new Rect(mainWindow.mContentInsets);
+            final Rect insets = new Rect();
+            mainWindow.getContentInsets(insets);
             InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6c8572a..b7507a4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -303,7 +303,7 @@
     private Rect getInsets(WindowState state) {
         // XXX(b/72757033): These are insets relative to the window frame, but we're really
         // interested in the insets relative to the task bounds.
-        final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
+        final Rect insets = minRect(state.getContentInsets(), state.getStableInsets());
         InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
         return insets;
     }
@@ -373,7 +373,7 @@
         node.setClipToBounds(false);
         final DisplayListCanvas c = node.start(width, height);
         c.drawColor(color);
-        decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets);
+        decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
         decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
         node.end(c);
         final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
@@ -383,7 +383,7 @@
         // Note, the app theme snapshot is never translucent because we enforce a non-translucent
         // color above
         return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
-                topChild.getConfiguration().orientation, mainWindow.mStableInsets,
+                topChild.getConfiguration().orientation, mainWindow.getStableInsets(),
                 ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */,
                 false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
                 false);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 228bfad..9381fc6 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -18,20 +18,27 @@
 
 import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
 import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.CONTENT_INSETS;
 import static com.android.server.wm.WindowFramesProto.CUTOUT;
 import static com.android.server.wm.WindowFramesProto.DECOR_FRAME;
 import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
 import static com.android.server.wm.WindowFramesProto.FRAME;
+import static com.android.server.wm.WindowFramesProto.OUTSETS;
 import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME;
 import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowFramesProto.OVERSCAN_INSETS;
 import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.STABLE_INSETS;
 import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME;
+import static com.android.server.wm.WindowFramesProto.VISIBLE_INSETS;
 
 import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayCutout;
+import android.view.WindowManager;
 
+import com.android.server.wm.utils.InsetUtils;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
@@ -60,7 +67,7 @@
      *
      * TODO(b/111611553): The name is unclear and most likely should be swapped with
      * {@link #mParentFrame}
-    */
+     */
     public final Rect mDisplayFrame = new Rect();
 
     /**
@@ -118,6 +125,12 @@
      */
     final Rect mLastFrame = new Rect();
 
+    private boolean mFrameSizeChanged = false;
+
+    // Frame that is scaled to the application's coordinate space when in
+    // screen size compatibility mode.
+    final Rect mCompatFrame = new Rect();
+
     /**
      * Whether the parent frame would have been different if there was no display cutout.
      */
@@ -131,7 +144,52 @@
     /**
      * The last cutout that has been reported to the client.
      */
-    WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+    private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+    private boolean mDisplayCutoutChanged;
+
+    /**
+     * Insets that determine the area covered by the display overscan region.  These are in the
+     * application's coordinate space (without compatibility scale applied).
+     */
+    final Rect mOverscanInsets = new Rect();
+    final Rect mLastOverscanInsets = new Rect();
+    private boolean mOverscanInsetsChanged;
+
+    /**
+     * Insets that determine the area covered by the stable system windows.  These are in the
+     * application's coordinate space (without compatibility scale applied).
+     */
+    final Rect mStableInsets = new Rect();
+    final Rect mLastStableInsets = new Rect();
+    private boolean mStableInsetsChanged;
+
+    /**
+     * Outsets determine the area outside of the surface where we want to pretend that it's possible
+     * to draw anyway.
+     */
+    final Rect mOutsets = new Rect();
+    final Rect mLastOutsets = new Rect();
+    private boolean mOutsetsChanged = false;
+
+    /**
+     * Insets that determine the actually visible area.  These are in the application's
+     * coordinate space (without compatibility scale applied).
+     */
+    final Rect mVisibleInsets = new Rect();
+    final Rect mLastVisibleInsets = new Rect();
+    private boolean mVisibleInsetsChanged;
+
+    /**
+     * Insets that are covered by system windows (such as the status bar) and
+     * transient docking windows (such as the IME).  These are in the application's
+     * coordinate space (without compatibility scale applied).
+     */
+    final Rect mContentInsets = new Rect();
+    final Rect mLastContentInsets = new Rect();
+    private boolean mContentInsetsChanged;
+
+    private final Rect mTmpRect = new Rect();
 
     public WindowFrames() {
     }
@@ -171,15 +229,141 @@
     /**
      * @return true if the width or height has changed since last reported to the client.
      */
-    boolean didFrameSizeChange() {
+    private boolean didFrameSizeChange() {
         return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
     }
 
     /**
-     * @return true if the display cutout has changed since last reported to the client.
+     * Calculates the outsets for this windowFrame. The outsets are calculated by the area between
+     * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
+     * {@link #mOutsets} is set to empty.
+     *
+     * @param hasOutsets Whether this frame has outsets.
      */
-    boolean didDisplayCutoutChange() {
-        return !mLastDisplayCutout.equals(mDisplayCutout);
+    void calculateOutsets(boolean hasOutsets) {
+        if (hasOutsets) {
+            InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
+        } else {
+            mOutsets.setEmpty();
+        }
+    }
+
+    /**
+     * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+     *
+     * @param cutoutInsets The insets for the cutout.
+     */
+    void calculateDockedDividerInsets(Rect cutoutInsets) {
+        // For the docked divider, we calculate the stable insets like a full-screen window
+        // so it can use it to calculate the snap positions.
+        mTmpRect.set(mDisplayFrame);
+        mTmpRect.inset(cutoutInsets);
+        mTmpRect.intersectUnchecked(mStableFrame);
+        InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets);
+
+        // The divider doesn't care about insets in any case, so set it to empty so we don't
+        // trigger a relayout when moving it.
+        mContentInsets.setEmpty();
+        mVisibleInsets.setEmpty();
+        mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+    }
+
+    /**
+     * Calculate the insets for a window.
+     *
+     * @param windowsAreFloating    Whether the window is in a floating task such as pinned or
+     *                              freeform
+     * @param inFullscreenContainer Whether the window is in a container that takes up the screen's
+     *                              entire space
+     * @param windowBounds          The bounds for the window
+     */
+    void calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer,
+            Rect windowBounds) {
+        // Override right and/or bottom insets in case if the frame doesn't fit the screen in
+        // non-fullscreen mode.
+        boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
+                && mFrame.right > windowBounds.right;
+        boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
+                && mFrame.bottom > windowBounds.bottom;
+
+        mTmpRect.set(mFrame.left, mFrame.top, overrideRightInset ? mTmpRect.right : mFrame.right,
+                overrideBottomInset ? mTmpRect.bottom : mFrame.bottom);
+
+        InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets);
+        InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets);
+        InsetUtils.insetsBetweenFrames(mTmpRect, mStableFrame, mStableInsets);
+    }
+
+    /**
+     * Scales all the insets by a specific amount.
+     *
+     * @param scale The amount to scale the insets by.
+     */
+    void scaleInsets(float scale) {
+        mOverscanInsets.scale(scale);
+        mContentInsets.scale(scale);
+        mVisibleInsets.scale(scale);
+        mStableInsets.scale(scale);
+        mOutsets.scale(scale);
+    }
+
+    void offsetFrames(int layoutXDiff, int layoutYDiff) {
+        mFrame.offset(layoutXDiff, layoutYDiff);
+        mContentFrame.offset(layoutXDiff, layoutYDiff);
+        mVisibleFrame.offset(layoutXDiff, layoutYDiff);
+        mStableFrame.offset(layoutXDiff, layoutYDiff);
+    }
+
+    /**
+     * Updates info about whether the size of the window has changed since last reported.
+     *
+     * @return true if info about size has changed since last reported.
+     */
+    boolean setReportResizeHints() {
+        mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
+        mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
+        mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
+        mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
+        mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
+        mFrameSizeChanged |= didFrameSizeChange();
+        mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
+        return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
+                || mStableInsetsChanged || mOutsetsChanged || mFrameSizeChanged
+                || mDisplayCutoutChanged;
+    }
+
+    /**
+     * Resets the insets changed flags so they're all set to false again. This should be called
+     * after the insets are reported to client.
+     */
+    void resetInsetsChanged() {
+        mOverscanInsetsChanged = false;
+        mContentInsetsChanged = false;
+        mVisibleInsetsChanged = false;
+        mStableInsetsChanged = false;
+        mOutsetsChanged = false;
+        mFrameSizeChanged = false;
+        mDisplayCutoutChanged = false;
+    }
+
+    /**
+     * Copy over inset values as the last insets that were sent to the client.
+     */
+    void updateLastInsetValues() {
+        mLastOverscanInsets.set(mOverscanInsets);
+        mLastContentInsets.set(mContentInsets);
+        mLastVisibleInsets.set(mVisibleInsets);
+        mLastStableInsets.set(mStableInsets);
+        mLastOutsets.set(mOutsets);
+        mLastDisplayCutout = mDisplayCutout;
+    }
+
+    /**
+     * Sets the last content insets as (-1, -1, -1, -1) to force the next layout pass to update
+     * the client.
+     */
+    void resetLastContentInsets() {
+        mLastContentInsets.set(-1, -1, -1, -1);
     }
 
     public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
@@ -194,6 +378,12 @@
         mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
         mFrame.writeToProto(proto, FRAME);
         mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
+        mContentInsets.writeToProto(proto, CONTENT_INSETS);
+        mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
+        mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
+        mStableInsets.writeToProto(proto, STABLE_INSETS);
+        mOutsets.writeToProto(proto, OUTSETS);
+
         proto.end(token);
     }
 
@@ -211,5 +401,34 @@
                 + " last=" + mLastFrame.toShortString(sTmpSB));
         pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout()
                 + " last=" + mLastDisplayCutout.getDisplayCutout());
+        pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB)
+                + " content=" + mContentInsets.toShortString(sTmpSB)
+                + " visible=" + mVisibleInsets.toShortString(sTmpSB)
+                + " stable=" + mStableInsets.toShortString(sTmpSB)
+                + " outsets=" + mOutsets.toShortString(sTmpSB));
+        pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB)
+                + " content=" + mLastContentInsets.toShortString(sTmpSB)
+                + " visible=" + mLastVisibleInsets.toShortString(sTmpSB)
+                + " stable=" + mLastStableInsets.toShortString(sTmpSB)
+                + " outset=" + mLastOutsets.toShortString(sTmpSB));
+    }
+
+    String getInsetsInfo() {
+        return "ci=" + mContentInsets.toShortString()
+                + " vi=" + mVisibleInsets.toShortString()
+                + " si=" + mStableInsets.toShortString()
+                + " of=" + mOutsets.toShortString();
+    }
+
+    String getInsetsChangedInfo() {
+        return "contentInsetsChanged=" + mContentInsetsChanged
+                + " " + mContentInsets.toShortString()
+                + " visibleInsetsChanged=" + mVisibleInsetsChanged
+                + " " + mVisibleInsets.toShortString()
+                + " stableInsetsChanged=" + mStableInsetsChanged
+                + " " + mStableInsets.toShortString()
+                + " outsetsChanged=" + mOutsetsChanged
+                + " " + mOutsets.toShortString()
+                + " displayCutoutChanged=" + mDisplayCutoutChanged;
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e80a47e..679e0d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -734,8 +734,9 @@
     final DisplayManagerInternal mDisplayManagerInternal;
     final DisplayManager mDisplayManager;
 
-    // Indicates whether this device supports wide color gamut rendering
+    // Indicates whether this device supports wide color gamut / HDR rendering
     private boolean mHasWideColorGamutSupport;
+    private boolean mHasHdrSupport;
 
     // Who is holding the screen on.
     private Session mHoldingScreenOn;
@@ -2167,14 +2168,10 @@
             // The last inset values represent the last client state.
             win.updateLastInsetValues();
 
-            outFrame.set(win.mCompatFrame);
-            outOverscanInsets.set(win.mOverscanInsets);
-            outContentInsets.set(win.mContentInsets);
-            win.mLastRelayoutContentInsets.set(win.mContentInsets);
-            outVisibleInsets.set(win.mVisibleInsets);
-            outStableInsets.set(win.mStableInsets);
+            win.getCompatFrame(outFrame);
+            win.getInsetsForRelayout(outOverscanInsets, outContentInsets, outVisibleInsets,
+                    outStableInsets, outOutsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
-            outOutsets.set(win.mOutsets);
             outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
             if (localLOGV) Slog.v(
                 TAG_WM, "Relayout given client " + client.asBinder()
@@ -4504,6 +4501,7 @@
         mPolicy.systemReady();
         mTaskSnapshotController.systemReady();
         mHasWideColorGamutSupport = queryWideColorGamutSupport();
+        mHasHdrSupport = queryHdrSupport();
     }
 
     private static boolean queryWideColorGamutSupport() {
@@ -4519,6 +4517,19 @@
         return false;
     }
 
+    private static boolean queryHdrSupport() {
+        try {
+            ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
+            OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay();
+            if (hasHdr != null) {
+                return hasHdr.value;
+            }
+        } catch (RemoteException e) {
+            // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
+        }
+        return false;
+    }
+
     // -------------------------------------------------------------
     // Async Handler
     // -------------------------------------------------------------
@@ -5993,11 +6004,10 @@
     }
 
     @Override
-    public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) {
+    public void createInputConsumer(IBinder token, String name, int displayId,
+            InputChannel inputChannel) {
         synchronized (mWindowMap) {
-            // TODO(b/112049699): Fix this for multiple displays. There is only one inputChannel
-            // here to accept the return value.
-            DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+            DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display != null) {
                 display.getInputMonitor().createInputConsumer(token, name, inputChannel,
                         Binder.getCallingPid(), Binder.getCallingUserHandle());
@@ -6006,11 +6016,9 @@
     }
 
     @Override
-    public boolean destroyInputConsumer(String name) {
+    public boolean destroyInputConsumer(String name, int displayId) {
         synchronized (mWindowMap) {
-            // TODO(b/112049699): Fix this for multiple displays. For consistency with
-            // createInputConsumer above.
-            DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+            DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display != null) {
                 return display.getInputMonitor().destroyInputConsumer(name);
             }
@@ -7499,6 +7507,10 @@
                 SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1;
     }
 
+    boolean hasHdrSupport() {
+        return mHasHdrSupport && hasWideColorGamutSupport();
+    }
+
     void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
         if (!win.hideNonSystemOverlayWindowsWhenVisible()
                 && !mHidingNonSystemOverlayWindows.contains(win)) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 466e298..637c0ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -120,7 +120,6 @@
 import static com.android.server.wm.WindowStateProto.ANIMATOR;
 import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
 import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
 import static com.android.server.wm.WindowStateProto.DESTROYING;
 import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
 import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
@@ -131,20 +130,16 @@
 import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
-import static com.android.server.wm.WindowStateProto.OUTSETS;
-import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
 import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.REMOVED;
 import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
 import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
 import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.WindowStateProto.STABLE_INSETS;
 import static com.android.server.wm.WindowStateProto.STACK_ID;
 import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
 import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
 import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
 import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
 import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
 
@@ -200,6 +195,7 @@
 import com.android.server.input.InputWindowHandle;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import com.android.server.wm.utils.InsetUtils;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
@@ -309,22 +305,6 @@
     private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
 
     /**
-     * Insets that determine the actually visible area.  These are in the application's
-     * coordinate space (without compatibility scale applied).
-     */
-    final Rect mVisibleInsets = new Rect();
-    private final Rect mLastVisibleInsets = new Rect();
-    private boolean mVisibleInsetsChanged;
-
-    /**
-     * Insets that are covered by system windows (such as the status bar) and
-     * transient docking windows (such as the IME).  These are in the application's
-     * coordinate space (without compatibility scale applied).
-     */
-    final Rect mContentInsets = new Rect();
-    final Rect mLastContentInsets = new Rect();
-
-    /**
      * The last content insets returned to the client in relayout. We use
      * these in the bounds animation to ensure we only observe inset changes
      * at the same time that a client resizes it's surface so that we may use
@@ -333,34 +313,6 @@
      */
     final Rect mLastRelayoutContentInsets = new Rect();
 
-    private boolean mContentInsetsChanged;
-
-    /**
-     * Insets that determine the area covered by the display overscan region.  These are in the
-     * application's coordinate space (without compatibility scale applied).
-     */
-    final Rect mOverscanInsets = new Rect();
-    private final Rect mLastOverscanInsets = new Rect();
-    private boolean mOverscanInsetsChanged;
-
-    /**
-     * Insets that determine the area covered by the stable system windows.  These are in the
-     * application's coordinate space (without compatibility scale applied).
-     */
-    final Rect mStableInsets = new Rect();
-    private final Rect mLastStableInsets = new Rect();
-    private boolean mStableInsetsChanged;
-
-    /**
-     * Outsets determine the area outside of the surface where we want to pretend that it's possible
-     * to draw anyway.
-     */
-    final Rect mOutsets = new Rect();
-    private final Rect mLastOutsets = new Rect();
-    private boolean mOutsetsChanged = false;
-
-    private boolean mDisplayCutoutChanged;
-
     /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
@@ -399,11 +351,6 @@
     float mLastHScale=1, mLastVScale=1;
     final Matrix mTmpMatrix = new Matrix();
 
-    private boolean mFrameSizeChanged = false;
-    // Frame that is scaled to the application's coordinate space when in
-    // screen size compatibility mode.
-    final Rect mCompatFrame = new Rect();
-
     private final WindowFrames mWindowFrames = new WindowFrames();
 
     /**
@@ -988,17 +935,7 @@
         applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
 
         // Calculate the outsets before the content frame gets shrinked to the window frame.
-        if (hasOutsets) {
-            mOutsets.set(
-                    Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0),
-                    Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0),
-                    Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right,
-                            0),
-                    Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom,
-                            0));
-        } else {
-            mOutsets.set(0, 0, 0, 0);
-        }
+        mWindowFrames.calculateOutsets(hasOutsets);
 
         // Make sure the content and visible frames are inside of the
         // final window frame.
@@ -1055,90 +992,35 @@
         if (inFullscreenContainer && !windowsAreFloating) {
             // Windows that are not fullscreen can be positioned outside of the display frame,
             // but that is not a reason to provide them with overscan insets.
-            mOverscanInsets.set(
-                    Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0),
-                    Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0),
-                    Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0),
-                    Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom,
-                            0));
+            InsetUtils.insetsBetweenFrames(layoutContainingFrame, mWindowFrames.mOverscanFrame,
+                    mWindowFrames.mOverscanInsets);
         }
 
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
-            // For the docked divider, we calculate the stable insets like a full-screen window
-            // so it can use it to calculate the snap positions.
             final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
                     mWindowFrames.mDisplayFrame);
-            mTmpRect.set(mWindowFrames.mDisplayFrame);
-            mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
-            mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame);
-
-            mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0),
-                    Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0),
-                    Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0),
-                    Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0));
-
-            // The divider doesn't care about insets in any case, so set it to empty so we don't
-            // trigger a relayout when moving it.
-            mContentInsets.setEmpty();
-            mVisibleInsets.setEmpty();
-            windowFrames.setDisplayCutout(WmDisplayCutout.NO_CUTOUT);
+            mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
         } else {
             getDisplayContent().getBounds(mTmpRect);
-            // Override right and/or bottom insets in case if the frame doesn't fit the screen in
-            // non-fullscreen mode.
-            boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
-                    && mWindowFrames.mFrame.right > mTmpRect.right;
-            boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
-                    && mWindowFrames.mFrame.bottom > mTmpRect.bottom;
-            mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left,
-                    mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top,
-                    overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right
-                            : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right,
-                    overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom
-                            : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom);
-
-            mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left,
-                    mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top,
-                    overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right
-                            : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right,
-                    overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom
-                            : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom);
-
-            mStableInsets.set(
-                    Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0),
-                    Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0),
-                    overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right,
-                            0) : Math.max(
-                            mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0),
-                    overrideBottomInset ? Math.max(
-                            mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max(
-                            mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0));
+            mWindowFrames.calculateInsets(windowsAreFloating, inFullscreenContainer, mTmpRect);
         }
 
         mWindowFrames.setDisplayCutout(
                 windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
 
         // Offset the actual frame by the amount layout frame is off.
-        mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff);
-        mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
-        mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff);
-        mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
-        mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+        mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
 
-        mCompatFrame.set(mWindowFrames.mFrame);
+        mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
             // window, we need to apply this to its insets so that they are
             // reported to the app in its coordinate space.
-            mOverscanInsets.scale(mInvGlobalScale);
-            mContentInsets.scale(mInvGlobalScale);
-            mVisibleInsets.scale(mInvGlobalScale);
-            mStableInsets.scale(mInvGlobalScale);
-            mOutsets.scale(mInvGlobalScale);
+            mWindowFrames.scaleInsets(mInvGlobalScale);
 
             // Also the scaled frame that we report to the app needs to be
             // adjusted to be in its coordinate space.
-            mCompatFrame.scale(mInvGlobalScale);
+            mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
         }
 
         if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
@@ -1156,10 +1038,7 @@
                 + mRequestedWidth + ", mRequestedheight="
                 + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
                 + "): frame=" + mWindowFrames.mFrame.toShortString()
-                + " ci=" + mContentInsets.toShortString()
-                + " vi=" + mVisibleInsets.toShortString()
-                + " si=" + mStableInsets.toShortString()
-                + " of=" + mOutsets.toShortString());
+                + " " + mWindowFrames.getInsetsInfo());
     }
 
     // TODO: Look into whether this override is still necessary.
@@ -1219,6 +1098,14 @@
         return mWindowFrames.mDisplayCutout;
     }
 
+    void getCompatFrame(Rect outFrame) {
+        outFrame.set(mWindowFrames.mCompatFrame);
+    }
+
+    void getCompatFrameSize(Rect outFrame) {
+        outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
+    }
+
     @Override
     public boolean getGivenInsetsPendingLw() {
         return mGivenInsetsPending;
@@ -1270,15 +1157,7 @@
     }
 
     boolean setReportResizeHints() {
-        mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
-        mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
-        mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
-        mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
-        mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
-        mFrameSizeChanged |= mWindowFrames.didFrameSizeChange();
-        mDisplayCutoutChanged |= mWindowFrames.didDisplayCutoutChange();
-        return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
-                || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
+        return mWindowFrames.setReportResizeHints();
     }
 
     /**
@@ -1301,7 +1180,7 @@
             return;
         }
 
-        setReportResizeHints();
+        boolean didFrameInsetsChange = setReportResizeHints();
         boolean configChanged = isConfigChanged();
         if (DEBUG_CONFIGURATION && configChanged) {
             Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
@@ -1318,31 +1197,18 @@
         // variables, because mFrameSizeChanged only tracks the width and height changing.
         mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
 
-        if (mContentInsetsChanged
-                || mVisibleInsetsChanged
-                || mStableInsetsChanged
+        if (didFrameInsetsChange
                 || winAnimator.mSurfaceResized
-                || mOutsetsChanged
-                || mFrameSizeChanged
-                || mDisplayCutoutChanged
                 || configChanged
                 || dragResizingChanged
                 || mReportOrientationChanged) {
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
                 Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
-                        + " contentInsetsChanged=" + mContentInsetsChanged
-                        + " " + mContentInsets.toShortString()
-                        + " visibleInsetsChanged=" + mVisibleInsetsChanged
-                        + " " + mVisibleInsets.toShortString()
-                        + " stableInsetsChanged=" + mStableInsetsChanged
-                        + " " + mStableInsets.toShortString()
-                        + " outsetsChanged=" + mOutsetsChanged
-                        + " " + mOutsets.toShortString()
+                        + " " + mWindowFrames.getInsetsChangedInfo()
                         + " surfaceResized=" + winAnimator.mSurfaceResized
                         + " configChanged=" + configChanged
                         + " dragResizingChanged=" + dragResizingChanged
-                        + " reportOrientationChanged=" + mReportOrientationChanged
-                        + " displayCutoutChanged=" + mDisplayCutoutChanged);
+                        + " reportOrientationChanged=" + mReportOrientationChanged);
             }
 
             // If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -3015,7 +2881,7 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
         try {
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
-                    + ": " + mCompatFrame);
+                    + ": " + mWindowFrames.mCompatFrame);
             final MergedConfiguration mergedConfiguration =
                     new MergedConfiguration(mService.mRoot.getConfiguration(),
                     getMergedOverrideConfiguration());
@@ -3026,11 +2892,11 @@
                 Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
 
             final Rect frame = mWindowFrames.mFrame;
-            final Rect overscanInsets = mLastOverscanInsets;
-            final Rect contentInsets = mLastContentInsets;
-            final Rect visibleInsets = mLastVisibleInsets;
-            final Rect stableInsets = mLastStableInsets;
-            final Rect outsets = mLastOutsets;
+            final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
+            final Rect contentInsets = mWindowFrames.mLastContentInsets;
+            final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
+            final Rect stableInsets = mWindowFrames.mLastStableInsets;
+            final Rect outsets = mWindowFrames.mLastOutsets;
             final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
             final boolean reportOrientation = mReportOrientationChanged;
             final int displayId = getDisplayId();
@@ -3061,13 +2927,7 @@
                 mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
             }
 
-            mOverscanInsetsChanged = false;
-            mContentInsetsChanged = false;
-            mVisibleInsetsChanged = false;
-            mStableInsetsChanged = false;
-            mOutsetsChanged = false;
-            mFrameSizeChanged = false;
-            mDisplayCutoutChanged = false;
+            mWindowFrames.resetInsetsChanged();
             mWinAnimator.mSurfaceResized = false;
             mReportOrientationChanged = false;
         } catch (RemoteException e) {
@@ -3293,7 +3153,6 @@
         mAttrs.writeToProto(proto, ATTRIBUTES);
         mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
         mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
-        mContentInsets.writeToProto(proto, CONTENT_INSETS);
         mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
         mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
         mWinAnimator.writeToProto(proto, ANIMATOR);
@@ -3307,10 +3166,6 @@
         proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
         proto.write(HAS_SURFACE, mHasSurface);
         proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
-        mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
-        mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
-        mStableInsets.writeToProto(proto, STABLE_INSETS);
-        mOutsets.writeToProto(proto, OUTSETS);
         proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
         proto.write(DESTROYING, mDestroying);
         proto.write(REMOVED, mRemoved);
@@ -3417,21 +3272,11 @@
                 + " isReadyForDisplay()=" + isReadyForDisplay()
                 + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
         if (mEnforceSizeCompat) {
-            pw.println(prefix + "mCompatFrame=" + mCompatFrame.toShortString(sTmpSB));
+            pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
         }
         if (dumpAll) {
             mWindowFrames.dump(pw, prefix);
-            pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB)
-                    + " content=" + mContentInsets.toShortString(sTmpSB)
-                    + " visible=" + mVisibleInsets.toShortString(sTmpSB)
-                    + " stable=" + mStableInsets.toShortString(sTmpSB)
-                    + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB)
-                    + " outsets=" + mOutsets.toShortString(sTmpSB));
-            pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB)
-                    + " content=" + mLastContentInsets.toShortString(sTmpSB)
-                    + " visible=" + mLastVisibleInsets.toShortString(sTmpSB)
-                    + " stable=" + mLastStableInsets.toShortString(sTmpSB)
-                    + " outset=" + mLastOutsets.toShortString(sTmpSB));
+            pw.println(prefix + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB));
         }
         super.dump(pw, prefix, dumpAll);
         pw.println(prefix + mWinAnimator + ":");
@@ -3531,7 +3376,7 @@
         }
     }
 
-    void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
+    private void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
         final int pw = containingFrame.width();
         final int ph = containingFrame.height();
         final Task task = getTask();
@@ -3609,10 +3454,10 @@
 
         // We need to make sure we update the CompatFrame as it is used for
         // cropping decisions, etc, on systems where we lack a decor layer.
-        mCompatFrame.set(mWindowFrames.mFrame);
+        mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
         if (mEnforceSizeCompat) {
             // See comparable block in computeFrameLw.
-            mCompatFrame.scale(mInvGlobalScale);
+            mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
         }
     }
 
@@ -4322,13 +4167,15 @@
             // On a different display there is no system decor. Crop the window
             // by the screen boundaries.
             // TODO(multi-display)
-            policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
-            policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
-                    displayInfo.logicalWidth - mCompatFrame.left,
-                    displayInfo.logicalHeight - mCompatFrame.top);
+            policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
+                    mWindowFrames.mCompatFrame.height());
+            policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
+                    displayInfo.logicalWidth - mWindowFrames.mCompatFrame.left,
+                    displayInfo.logicalHeight - mWindowFrames.mCompatFrame.top);
         } else if (skipDecorCrop()) {
             // Windows without policy decor aren't cropped.
-            policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
+            policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
+                    mWindowFrames.mCompatFrame.height());
         } else {
             // Crop to the system decor specified by policy.
             calculateSystemDecorRect(policyCrop);
@@ -4486,12 +4333,7 @@
      * Updates the last inset values to the current ones.
      */
     void updateLastInsetValues() {
-        mLastOverscanInsets.set(mOverscanInsets);
-        mLastContentInsets.set(mContentInsets);
-        mLastVisibleInsets.set(mVisibleInsets);
-        mLastStableInsets.set(mStableInsets);
-        mLastOutsets.set(mOutsets);
-        mWindowFrames.mLastDisplayCutout = mWindowFrames.mDisplayCutout;
+        mWindowFrames.updateLastInsetValues();
     }
 
     void startAnimation(Animation anim) {
@@ -4880,6 +4722,44 @@
         }
     }
 
+    /**
+     * Copy the inset values over so they can be sent back to the client when a relayout occurs.
+     */
+    void getInsetsForRelayout(Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
+            Rect outStableInsets, Rect outOutsets) {
+        outOverscanInsets.set(mWindowFrames.mOverscanInsets);
+        outContentInsets.set(mWindowFrames.mContentInsets);
+        outVisibleInsets.set(mWindowFrames.mVisibleInsets);
+        outStableInsets.set(mWindowFrames.mStableInsets);
+        outOutsets.set(mWindowFrames.mOutsets);
+
+        mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
+    }
+
+    void getContentInsets(Rect outContentInsets) {
+        outContentInsets.set(mWindowFrames.mContentInsets);
+    }
+
+    Rect getContentInsets() {
+        return mWindowFrames.mContentInsets;
+    }
+
+    void getStableInsets(Rect outStableInsets) {
+        outStableInsets.set(mWindowFrames.mStableInsets);
+    }
+
+    Rect getStableInsets() {
+        return mWindowFrames.mStableInsets;
+    }
+
+    void resetLastContentInsets() {
+        mWindowFrames.resetLastContentInsets();
+    }
+
+    Rect getVisibleInsets() {
+        return mWindowFrames.mVisibleInsets;
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b158ae2..c80eb86 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
+
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
@@ -46,7 +47,6 @@
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
 import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
 
 import android.content.Context;
 import android.graphics.Matrix;
@@ -476,8 +476,7 @@
             flags |= SurfaceControl.SECURE;
         }
 
-        mTmpSize.set(0, 0, 0, 0);
-        calculateSurfaceBounds(w, attrs);
+        calculateSurfaceBounds(w, attrs, mTmpSize);
         final int width = mTmpSize.width();
         final int height = mTmpSize.height();
 
@@ -556,44 +555,38 @@
         return mSurfaceController;
     }
 
-    private void calculateSurfaceBounds(WindowState w, LayoutParams attrs) {
+    private void calculateSurfaceBounds(WindowState w, LayoutParams attrs, Rect outSize) {
+        outSize.setEmpty();
         if ((attrs.flags & FLAG_SCALED) != 0) {
             // For a scaled surface, we always want the requested size.
-            mTmpSize.right = mTmpSize.left + w.mRequestedWidth;
-            mTmpSize.bottom = mTmpSize.top + w.mRequestedHeight;
+            outSize.right = w.mRequestedWidth;
+            outSize.bottom = w.mRequestedHeight;
         } else {
             // When we're doing a drag-resizing, request a surface that's fullscreen size,
             // so that we don't need to reallocate during the process. This also prevents
             // buffer drops due to size mismatch.
             if (w.isDragResizing()) {
-                if (w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
-                    mTmpSize.left = 0;
-                    mTmpSize.top = 0;
-                }
                 final DisplayInfo displayInfo = w.getDisplayInfo();
-                mTmpSize.right = mTmpSize.left + displayInfo.logicalWidth;
-                mTmpSize.bottom = mTmpSize.top + displayInfo.logicalHeight;
+                outSize.right = displayInfo.logicalWidth;
+                outSize.bottom = displayInfo.logicalHeight;
             } else {
-                mTmpSize.right = mTmpSize.left + w.mCompatFrame.width();
-                mTmpSize.bottom = mTmpSize.top + w.mCompatFrame.height();
+                w.getCompatFrameSize(outSize);
             }
         }
 
         // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values.
         // This doesn't necessarily mean that there is an error in the system. The sizes might be
         // incorrect, because it is before the first layout or draw.
-        if (mTmpSize.width() < 1) {
-            mTmpSize.right = mTmpSize.left + 1;
+        if (outSize.width() < 1) {
+            outSize.right = 1;
         }
-        if (mTmpSize.height() < 1) {
-            mTmpSize.bottom = mTmpSize.top + 1;
+        if (outSize.height() < 1) {
+            outSize.bottom = 1;
         }
 
         // Adjust for surface insets.
-        mTmpSize.left -= attrs.surfaceInsets.left;
-        mTmpSize.top -= attrs.surfaceInsets.top;
-        mTmpSize.right += attrs.surfaceInsets.right;
-        mTmpSize.bottom += attrs.surfaceInsets.bottom;
+        outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
+                -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
     }
 
     boolean hasSurface() {
@@ -870,8 +863,7 @@
         final LayoutParams attrs = mWin.getAttrs();
         final Task task = w.getTask();
 
-        mTmpSize.set(0, 0, 0, 0);
-        calculateSurfaceBounds(w, attrs);
+        calculateSurfaceBounds(w, attrs, mTmpSize);
 
         mExtraHScale = (float) 1.0;
         mExtraVScale = (float) 1.0;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b467d61..42ade38 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -210,7 +210,7 @@
             const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
-    void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray);
+    void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
     void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiVisibility(int32_t visibility);
@@ -736,7 +736,8 @@
     }
 }
 
-void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray,
+         int32_t displayId) {
     Vector<sp<InputWindowHandle> > windowHandles;
 
     if (windowHandleObjArray) {
@@ -756,7 +757,7 @@
         }
     }
 
-    mInputManager->getDispatcher()->setInputWindows(windowHandles);
+    mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId);
 
     // Do this after the dispatcher has updated the window handle state.
     bool newPointerGesturesEnabled = true;
@@ -1446,10 +1447,10 @@
 }
 
 static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobjectArray windowHandleObjArray) {
+        jlong ptr, jobjectArray windowHandleObjArray, jint displayId) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    im->setInputWindows(env, windowHandleObjArray);
+    im->setInputWindows(env, windowHandleObjArray, displayId);
 }
 
 static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
@@ -1678,7 +1679,7 @@
             (void*) nativeInjectInputEvent },
     { "nativeToggleCapsLock", "(JI)V",
             (void*) nativeToggleCapsLock },
-    { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
+    { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
             (void*) nativeSetInputWindows },
     { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
             (void*) nativeSetFocusedApplication },
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ecc13b2..b9f8fdb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -743,10 +743,20 @@
             traceEnd();
         }
 
+        // Tracks and caches the device state.
+        traceBeginAndSlog("StartCachedDeviceStateService");
+        mSystemServiceManager.startService(CachedDeviceStateService.class);
+        traceEnd();
+
         // Tracks cpu time spent in binder calls
         traceBeginAndSlog("StartBinderCallsStatsService");
         mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
         traceEnd();
+
+        // Tracks time spent in handling messages in handlers.
+        traceBeginAndSlog("StartLooperStatsService");
+        mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);
+        traceEnd();
     }
 
     /**
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 3cdef1e..ccc092d 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -1385,6 +1385,20 @@
 
     private boolean startIpReachabilityMonitor() {
         try {
+            // TODO: Fetch these parameters from settings, and install a
+            // settings observer to watch for update and re-program these
+            // parameters (Q: is this level of dynamic updatability really
+            // necessary or does reading from settings at startup suffice?).
+            final int NUM_SOLICITS = 5;
+            final int INTER_SOLICIT_INTERVAL_MS = 750;
+            setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
+                    NUM_SOLICITS, INTER_SOLICIT_INTERVAL_MS);
+        } catch (Exception e) {
+            mLog.e("Failed to adjust neighbor parameters", e);
+            // Carry on using the system defaults (currently: 3, 1000);
+        }
+
+        try {
             mIpReachabilityMonitor = new IpReachabilityMonitor(
                     mContext,
                     mInterfaceParams,
@@ -1863,6 +1877,20 @@
         }
     }
 
+    private static void setNeighborParameters(
+            INetd netd, String ifName, int num_solicits, int inter_solicit_interval_ms)
+            throws RemoteException, IllegalArgumentException {
+        Preconditions.checkNotNull(netd);
+        Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
+        Preconditions.checkArgument(num_solicits > 0);
+        Preconditions.checkArgument(inter_solicit_interval_ms > 0);
+
+        for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
+            netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms", Integer.toString(inter_solicit_interval_ms));
+            netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit", Integer.toString(num_solicits));
+        }
+    }
+
     // TODO: extract out into CollectionUtils.
     static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
         for (T t : coll) {
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
new file mode 100644
index 0000000..34de9df
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    services.core \
+    services.devicepolicy \
+    frameworks-base-testutils \
+    androidx-test \
+    mockito-target-extended-minus-junit4 \
+    ShortcutManagerTestUtils \
+    compatibility-device-util \
+    truth-prebuilt
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.mock
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libdexmakerjvmtiagent \
+    libstaticjvmtiagent
+
+LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
new file mode 100644
index 0000000..247e446
--- /dev/null
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.mockingservicestests">
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.mockingservicestests"
+        android:label="Frameworks Mocking Services Tests" />
+</manifest>
diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml
new file mode 100644
index 0000000..adfee96
--- /dev/null
+++ b/services/tests/mockingservicestests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks Services Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksMockingServicesTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="FrameworksMockingServicesTests" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.mockingservicestests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 910aad7f..c8e6782 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -64,9 +64,6 @@
 import android.util.ArraySet;
 import android.util.Pair;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.server.AppStateTracker.Listener;
@@ -88,11 +85,14 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 /**
  * Tests for {@link AppStateTracker}
  *
  * Run with:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -683,10 +683,10 @@
 
         //--------------------------------------------------
         List<OpEntry> entries = new ArrayList<>();
-        entries.add(new AppOpsManager.OpEntry(
+        entries.add(new OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
-        entries.add(new AppOpsManager.OpEntry(
+        entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
 
@@ -694,7 +694,7 @@
 
         //--------------------------------------------------
         entries = new ArrayList<>();
-        entries.add(new AppOpsManager.OpEntry(
+        entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
 
@@ -702,7 +702,7 @@
 
         //--------------------------------------------------
         entries = new ArrayList<>();
-        entries.add(new AppOpsManager.OpEntry(
+        entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
 
@@ -710,10 +710,10 @@
 
         //--------------------------------------------------
         entries = new ArrayList<>();
-        entries.add(new AppOpsManager.OpEntry(
+        entries.add(new OpEntry(
                 AppStateTracker.TARGET_OP,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
-        entries.add(new AppOpsManager.OpEntry(
+        entries.add(new OpEntry(
                 AppOpsManager.OP_ACCESS_NOTIFICATIONS,
                 AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
 
@@ -754,7 +754,7 @@
         final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
-        AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
+        Listener l = mock(Listener.class);
         instance.addListener(l);
 
         // Power save on.
@@ -797,7 +797,7 @@
         final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
 
-        AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
+        Listener l = mock(Listener.class);
         instance.addListener(l);
 
         // -------------------------------------------------------------------------
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
new file mode 100644
index 0000000..81107cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+
+import static org.mockito.Mockito.when;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.IPowerManager;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
+import android.os.RemoteException;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CachedDeviceStateService}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CachedDeviceStateServiceTest {
+    @Mock private BatteryManagerInternal mBatteryManager;
+    @Mock private IPowerManager mPowerManager;
+    private BroadcastInterceptingContext mContext;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        Context context = InstrumentationRegistry.getContext();
+        PowerManager powerManager = new PowerManager(context, mPowerManager, null);
+        mContext = new BroadcastInterceptingContext(context) {
+            @Override
+            public Object getSystemService(String name) {
+                switch (name) {
+                    case Context.POWER_SERVICE:
+                        return powerManager;
+                    default:
+                        return super.getSystemService(name);
+                }
+            }
+        };
+
+        LocalServices.addService(BatteryManagerInternal.class, mBatteryManager);
+
+        when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+    }
+
+    @After
+    public void tearDown() {
+        // Added by the CachedDeviceStateService.onStart().
+        LocalServices.removeServiceForTest(CachedDeviceState.Readonly.class);
+
+        // Added in @Before.
+        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+    }
+
+    @Test
+    public void correctlyReportsScreenInteractive() throws RemoteException {
+        CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+        when(mPowerManager.isInteractive()).thenReturn(true); // Screen on.
+
+        service.onStart();
+        CachedDeviceState.Readonly deviceState =
+                LocalServices.getService(CachedDeviceState.Readonly.class);
+
+        // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+        assertThat(deviceState.isScreenInteractive()).isFalse();
+
+        service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        assertThat(deviceState.isScreenInteractive()).isTrue();
+
+        mContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+        assertThat(deviceState.isScreenInteractive()).isFalse();
+
+        mContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+        assertThat(deviceState.isScreenInteractive()).isTrue();
+    }
+
+    @Test
+    public void correctlyReportsCharging() {
+        CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+        when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+        service.onStart();
+        CachedDeviceState.Readonly deviceState =
+                LocalServices.getService(CachedDeviceState.Readonly.class);
+
+        // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+        assertThat(deviceState.isCharging()).isTrue();
+
+        service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        assertThat(deviceState.isCharging()).isFalse();
+
+        Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        mContext.sendBroadcast(intentPluggedIn);
+        assertThat(deviceState.isCharging()).isTrue();
+
+        Intent intentUnplugged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intentUnplugged.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
+        mContext.sendBroadcast(intentUnplugged);
+        assertThat(deviceState.isCharging()).isFalse();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index ea3a3d0..e648230 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -133,15 +133,15 @@
     }
 
     private void assertContentInset(WindowState w, int left, int top, int right, int bottom) {
-        assertRect(w.mContentInsets, left, top, right, bottom);
+        assertRect(w.getContentInsets(), left, top, right, bottom);
     }
 
     private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) {
-        assertRect(w.mVisibleInsets, left, top, right, bottom);
+        assertRect(w.getVisibleInsets(), left, top, right, bottom);
     }
 
     private void assertStableInset(WindowState w, int left, int top, int right, int bottom) {
-        assertRect(w.mStableInsets, left, top, right, bottom);
+        assertRect(w.getStableInsets(), left, top, right, bottom);
     }
 
     private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 2f1c516..4874bce 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -44,6 +44,10 @@
     private static final String TAG = UsbAlsaManager.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    // Flag to turn on/off multi-peripheral select mode
+    // Set to true to have single-device-only mode
+    private static final boolean mIsSingleMode = true;
+
     private static final String ALSA_DIRECTORY = "/dev/snd/";
 
     private final Context mContext;
@@ -84,10 +88,11 @@
      */
     private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) {
         if (DEBUG) {
-            Slog.d(TAG, "selectAlsaDevice " + alsaDevice);
+            Slog.d(TAG, "selectAlsaDevice() " + alsaDevice);
         }
 
-        if (mSelectedDevice != null) {
+        // This must be where an existing USB audio device is deselected.... (I think)
+        if (mIsSingleMode && mSelectedDevice != null) {
             deselectAlsaDevice();
         }
 
@@ -104,9 +109,15 @@
 
         mSelectedDevice = alsaDevice;
         alsaDevice.start();
+        if (DEBUG) {
+            Slog.d(TAG, "selectAlsaDevice() - done.");
+        }
     }
 
     private synchronized void deselectAlsaDevice() {
+        if (DEBUG) {
+            Slog.d(TAG, "deselectAlsaDevice() mSelectedDevice " + mSelectedDevice);
+        }
         if (mSelectedDevice != null) {
             mSelectedDevice.stop();
             mSelectedDevice = null;
@@ -133,7 +144,7 @@
 
     /* package */ UsbAlsaDevice selectDefaultDevice() {
         if (DEBUG) {
-            Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
+            Slog.d(TAG, "selectDefaultDevice()");
         }
 
         if (mAlsaDevices.size() > 0) {
@@ -230,6 +241,8 @@
             }
         }
 
+        logDevices("deviceAdded()");
+
         if (DEBUG) {
             Slog.d(TAG, "deviceAdded() - done");
         }
@@ -254,6 +267,9 @@
             Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
             IoUtils.closeQuietly(usbMidiDevice);
         }
+
+        logDevices("usbDeviceRemoved()");
+
     }
 
    /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
@@ -296,6 +312,7 @@
     /**
      * Dump the USB alsa state.
      */
+    // invoked with "adb shell dumpsys usb"
     public void dump(DualDumpOutputStream dump, String idName, long id) {
         long token = dump.start(idName, id);
 
@@ -314,29 +331,26 @@
         dump.end(token);
     }
 
-/*
     public void logDevicesList(String title) {
-      if (DEBUG) {
-          for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
-              Slog.i(TAG, "UsbDevice-------------------");
-              Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]"));
-              Slog.i(TAG, "UsbAlsaDevice--------------");
-              Slog.i(TAG, "" + entry.getValue());
-          }
-      }
+        if (DEBUG) {
+            Slog.i(TAG, title + "----------------");
+            for (UsbAlsaDevice alsaDevice : mAlsaDevices) {
+                Slog.i(TAG, "  -->");
+                Slog.i(TAG, "" + alsaDevice);
+                Slog.i(TAG, "  <--");
+            }
+            Slog.i(TAG, "----------------");
+        }
     }
-*/
 
     // This logs a more terse (and more readable) version of the devices list
-/*
     public void logDevices(String title) {
-      if (DEBUG) {
-          Slog.i(TAG, title);
-          for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
-              Slog.i(TAG, entry.getValue().toShortString());
-          }
-      }
+        if (DEBUG) {
+            Slog.i(TAG, title + "----------------");
+            for (UsbAlsaDevice alsaDevice : mAlsaDevices) {
+                Slog.i(TAG, alsaDevice.toShortString());
+            }
+            Slog.i(TAG, "----------------");
+        }
     }
-*/
-
 }
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
new file mode 100644
index 0000000..1730d8f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * UsbResolveActivityManager creates UI dialogs for user to pick or confirm handler for
+ * usb attach event.
+ *
+ * @hide
+ */
+class UsbHandlerManager {
+    private static final String LOG_TAG = UsbHandlerManager.class.getSimpleName();
+
+    private final Context mContext;
+
+    UsbHandlerManager(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Shows dialog to user to allow them to optionally visit that URL for more
+     * information or software downloads if the attached USB accessory has a valid
+     * URL associated with it.
+     *
+     * @param accessory The accessory to confirm in the UI dialog
+     * @param user The user to start the UI dialog
+     */
+    void showUsbAccessoryUriActivity(@NonNull UsbAccessory accessory,
+            @NonNull UserHandle user) {
+        String uri = accessory.getUri();
+        if (uri != null && uri.length() > 0) {
+            // display URI to user
+            Intent dialogIntent = createDialogIntent();
+            dialogIntent.setClassName("com.android.systemui",
+                    "com.android.systemui.usb.UsbAccessoryUriActivity");
+            dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+            dialogIntent.putExtra("uri", uri);
+            try {
+                mContext.startActivityAsUser(dialogIntent, user);
+            } catch (ActivityNotFoundException e) {
+                Slog.e(LOG_TAG, "unable to start UsbAccessoryUriActivity");
+            }
+        }
+    }
+
+    /**
+     * Shows dialog to user to confirm the package to start when the USB device
+     * or accessory is attached and there is only one package claims to handle this
+     * USB device or accessory.
+     *
+     * @param rInfo The ResolveInfo of the package to confirm in the UI dialog
+     * @param device The USB device to confirm
+     * @param accessory The USB accessory to confirm
+     */
+    void confirmUsbHandler(@NonNull ResolveInfo rInfo, @Nullable UsbDevice device,
+            @Nullable UsbAccessory accessory) {
+        Intent resolverIntent = createDialogIntent();
+        // start UsbConfirmActivity if there is only one choice
+        resolverIntent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbConfirmActivity");
+        resolverIntent.putExtra("rinfo", rInfo);
+        UserHandle user =
+                UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
+
+        if (device != null) {
+            resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        } else {
+            resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        }
+
+        try {
+            mContext.startActivityAsUser(resolverIntent, user);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+        }
+    }
+
+    /**
+     * Shows dialog to user to select the package to start when the USB device
+     * or accessory is attached and there are more than one package claim to handle this
+     * USB device or accessory.
+     *
+     * @param matches The available resolutions of the intent
+     * @param user The user to start UI dialog
+     * @param intent The intent to start the UI dialog
+     */
+    void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
+            @NonNull UserHandle user, @NonNull Intent intent) {
+        Intent resolverIntent = createDialogIntent();
+        resolverIntent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbResolverActivity");
+        resolverIntent.putParcelableArrayListExtra("rlist", matches);
+        resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+
+        try {
+            mContext.startActivityAsUser(resolverIntent, user);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+        }
+    }
+
+    private Intent createDialogIntent() {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 67ad090..589bcdc 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -408,11 +408,15 @@
     /* Called from JNI in monitorUsbHostBus to report USB device removal */
     @SuppressWarnings("unused")
     private void usbDeviceRemoved(String deviceAddress) {
+        if (DEBUG) {
+            Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
+        }
+
         synchronized (mLock) {
             UsbDevice device = mDevices.remove(deviceAddress);
             if (device != null) {
                 Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
-                mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
+                mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
                 mSettingsManager.usbDeviceRemoved(device);
                 getCurrentUserSettings().usbDeviceRemoved(device);
 
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
new file mode 100644
index 0000000..2c9ee36
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.usb.UsbSettingsAccessoryPermissionProto;
+import android.service.usb.UsbSettingsDevicePermissionProto;
+import android.service.usb.UsbUserSettingsManagerProto;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.HashMap;
+
+/**
+ * UsbPermissionManager manages usb device or accessory access permissions.
+ *
+ * @hide
+ */
+class UsbPermissionManager {
+    private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
+
+    @GuardedBy("mLock")
+    /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
+    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+            new HashMap<>();
+    @GuardedBy("mLock")
+    /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
+    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+            new HashMap<>();
+
+    private final UserHandle mUser;
+    private final boolean mDisablePermissionDialogs;
+
+    private final Object mLock = new Object();
+
+    UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
+        mUser = user;
+        mDisablePermissionDialogs = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+    }
+
+    /**
+     * Removes access permissions of all packages for the USB accessory.
+     *
+     * @param accessory to remove permissions for
+     */
+    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+        synchronized (mLock) {
+            mAccessoryPermissionMap.remove(accessory);
+        }
+    }
+
+    /**
+     * Removes access permissions of all packages for the USB device.
+     *
+     * @param device to remove permissions for
+     */
+    void removeDevicePermissions(@NonNull UsbDevice device) {
+        synchronized (mLock) {
+            mDevicePermissionMap.remove(device.getDeviceName());
+        }
+    }
+
+    /**
+     * Grants permission for USB device without showing system dialog for package with uid.
+     *
+     * @param device to grant permission for
+     * @param uid to grant permission for
+     */
+    void grantDevicePermission(@NonNull UsbDevice device, int uid) {
+        synchronized (mLock) {
+            String deviceName = device.getDeviceName();
+            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mDevicePermissionMap.put(deviceName, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    /**
+     * Grants permission for USB accessory without showing system dialog for package with uid.
+     *
+     * @param accessory to grant permission for
+     * @param uid to grant permission for
+     */
+    void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
+        synchronized (mLock) {
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mAccessoryPermissionMap.put(accessory, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    /**
+     * Returns true if package with uid has permission to access the device.
+     *
+     * @param device to check permission for
+     * @param uid to check permission for
+     * @return {@code true} if package with uid has permission
+     */
+    boolean hasPermission(@NonNull UsbDevice device, int uid) {
+        synchronized (mLock) {
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    /**
+     * Returns true if caller has permission to access the accessory.
+     *
+     * @param accessory to check permission for
+     * @return {@code true} if caller has permssion
+     */
+    boolean hasPermission(@NonNull UsbAccessory accessory) {
+        synchronized (mLock) {
+            int uid = Binder.getCallingUid();
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    /**
+     * Creates UI dialog to request permission for the given package to access the device
+     * or accessory.
+     *
+     * @param device The USB device attached
+     * @param accessory The USB accessory attached
+     * @param canBeDefault Whether the calling pacakge can set as default handler
+     * of the USB device or accessory
+     * @param packageName The package name of the calling package
+     * @param uid The uid of the calling package
+     * @param userContext The context to start the UI dialog
+     * @param pi PendingIntent for returning result
+     */
+    void requestPermissionDialog(@Nullable UsbDevice device,
+                                 @Nullable UsbAccessory accessory,
+                                 boolean canBeDefault,
+                                 @NonNull String packageName,
+                                 int uid,
+                                 @NonNull Context userContext,
+                                 @NonNull PendingIntent pi) {
+        long identity = Binder.clearCallingIdentity();
+        Intent intent = new Intent();
+        if (device != null) {
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        } else {
+            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        }
+        intent.putExtra(Intent.EXTRA_INTENT, pi);
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+        intent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbPermissionActivity");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        try {
+            userContext.startActivityAsUser(intent, mUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    void dump(@NonNull DualDumpOutputStream dump) {
+        synchronized (mLock) {
+            for (String deviceName : mDevicePermissionMap.keySet()) {
+                long devicePermissionToken = dump.start("device_permissions",
+                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+
+                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+
+                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+                }
+
+                dump.end(devicePermissionToken);
+            }
+
+            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+                long accessoryPermissionToken = dump.start("accessory_permissions",
+                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+
+                dump.write("accessory_description",
+                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+                        accessory.getDescription());
+
+                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+                }
+
+                dump.end(accessoryPermissionToken);
+            }
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 7a906d0..1ab1f7e 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -193,6 +193,8 @@
 
     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
+    private final UsbHandlerManager mUsbHandlerManager;
+
     private final MtpNotificationManager mMtpNotificationManager;
 
     /**
@@ -201,9 +203,11 @@
      * @param context The context of the service
      * @param user The parent profile
      * @param settingsManager The settings manager of the service
+     * @param usbResolveActivityManager The resovle activity manager of the service
      */
     UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
-            @NonNull UsbSettingsManager settingsManager) {
+            @NonNull UsbSettingsManager settingsManager,
+            @NonNull UsbHandlerManager usbResolveActivityManager) {
         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
 
         Context parentUserContext;
@@ -238,6 +242,8 @@
                 parentUserContext,
                 device -> resolveActivity(createDeviceAttachedIntent(device),
                         device, false /* showMtpNotification */));
+
+        mUsbHandlerManager = usbResolveActivityManager;
     }
 
     /**
@@ -830,23 +836,8 @@
         // don't show the resolver activity if there are no choices available
         if (matches.size() == 0) {
             if (accessory != null) {
-                String uri = accessory.getUri();
-                if (uri != null && uri.length() > 0) {
-                    // display URI to user
-                    Intent dialogIntent = new Intent();
-                    dialogIntent.setClassName("com.android.systemui",
-                            "com.android.systemui.usb.UsbAccessoryUriActivity");
-                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                    dialogIntent.putExtra("uri", uri);
-                    try {
-                        mContext.startActivityAsUser(dialogIntent, mParentUser);
-                    } catch (ActivityNotFoundException e) {
-                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
-                    }
-                }
+                mUsbHandlerManager.showUsbAccessoryUriActivity(accessory, mParentUser);
             }
-
             // do nothing
             return;
         }
@@ -875,37 +866,10 @@
                 Slog.e(TAG, "startActivity failed", e);
             }
         } else {
-            Intent resolverIntent = new Intent();
-            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            UserHandle user;
-
             if (matches.size() == 1) {
-                ResolveInfo rInfo = matches.get(0);
-
-                // start UsbConfirmActivity if there is only one choice
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbConfirmActivity");
-                resolverIntent.putExtra("rinfo", rInfo);
-                user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
-
-                if (device != null) {
-                    resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
-                } else {
-                    resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                }
+                mUsbHandlerManager.confirmUsbHandler(matches.get(0), device, accessory);
             } else {
-                user = mParentUser;
-
-                // start UsbResolverActivity so user can choose an activity
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbResolverActivity");
-                resolverIntent.putParcelableArrayListExtra("rlist", matches);
-                resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
-            }
-            try {
-                mContext.startActivityAsUser(resolverIntent, user);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "unable to start activity " + resolverIntent, e);
+                mUsbHandlerManager.selectUsbHandler(matches, mParentUser, intent);
             }
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 9221825..27566f0 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -57,10 +57,12 @@
     private final SparseArray<UsbProfileGroupSettingsManager> mSettingsByProfileGroup
             = new SparseArray<>();
     private UserManager mUserManager;
+    private UsbHandlerManager mUsbHandlerManager;
 
     public UsbSettingsManager(@NonNull Context context) {
         mContext = context;
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mUsbHandlerManager = new UsbHandlerManager(context);
     }
 
     /**
@@ -74,7 +76,8 @@
         synchronized (mSettingsByUser) {
             UsbUserSettingsManager settings = mSettingsByUser.get(userId);
             if (settings == null) {
-                settings = new UsbUserSettingsManager(mContext, new UserHandle(userId));
+                settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
+                        new UsbPermissionManager(mContext, UserHandle.of(userId)));
                 mSettingsByUser.put(userId, settings);
             }
             return settings;
@@ -102,7 +105,8 @@
             UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.get(
                     parentUser.getIdentifier());
             if (settings == null) {
-                settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this);
+                settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this,
+                      mUsbHandlerManager);
                 mSettingsByProfileGroup.put(parentUser.getIdentifier(), settings);
             }
             return settings;
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 24a2d72..fe93399 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -21,15 +21,18 @@
 import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.res.XmlResourceParser;
 import android.hardware.usb.AccessoryFilter;
 import android.hardware.usb.DeviceFilter;
 import android.hardware.usb.UsbAccessory;
@@ -38,42 +41,34 @@
 import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.os.Binder;
-import android.os.Process;
 import android.os.UserHandle;
 import android.service.usb.UsbAccessoryAttachedActivities;
 import android.service.usb.UsbDeviceAttachedActivities;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
 import android.service.usb.UsbUserSettingsManagerProto;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
 
+import com.android.internal.util.XmlUtils;
 import com.android.internal.util.dump.DualDumpOutputStream;
 
+import org.xmlpull.v1.XmlPullParser;
+
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 class UsbUserSettingsManager {
-    private static final String TAG = "UsbUserSettingsManager";
+    private static final String TAG = UsbUserSettingsManager.class.getSimpleName();
     private static final boolean DEBUG = false;
 
     private final UserHandle mUser;
-    private final boolean mDisablePermissionDialogs;
 
     private final Context mUserContext;
     private final PackageManager mPackageManager;
-
-    // Temporary mapping USB device name to list of UIDs with permissions for the device
-    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
-            new HashMap<>();
-    // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
-    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
-            new HashMap<>();
+    private final UsbPermissionManager mUsbPermissionManager;
 
     private final Object mLock = new Object();
 
-    public UsbUserSettingsManager(Context context, UserHandle user) {
+    UsbUserSettingsManager(Context context, UserHandle user,
+            @NonNull UsbPermissionManager usbPermissionManager) {
         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
 
         try {
@@ -85,9 +80,7 @@
         mPackageManager = mUserContext.getPackageManager();
 
         mUser = user;
-
-        mDisablePermissionDialogs = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+        mUsbPermissionManager = usbPermissionManager;
     }
 
     /**
@@ -96,9 +89,7 @@
      * @param device The device the permissions are for
      */
     void removeDevicePermissions(@NonNull UsbDevice device) {
-        synchronized (mLock) {
-            mDevicePermissionMap.remove(device.getDeviceName());
-        }
+        mUsbPermissionManager.removeDevicePermissions(device);
     }
 
     /**
@@ -107,9 +98,7 @@
      * @param accessory The accessory the permissions are for
      */
     void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
-        synchronized (mLock) {
-            mAccessoryPermissionMap.remove(accessory);
-        }
+        mUsbPermissionManager.removeAccessoryPermissions(accessory);
     }
 
     /**
@@ -170,35 +159,17 @@
     }
 
     public boolean hasPermission(UsbDevice device, String packageName, int uid) {
-        synchronized (mLock) {
-            if (isCameraDevicePresent(device)) {
-                if (!isCameraPermissionGranted(packageName, uid)) {
-                    return false;
-                }
-            }
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
-            if (uidList == null) {
+        if (isCameraDevicePresent(device)) {
+            if (!isCameraPermissionGranted(packageName, uid)) {
                 return false;
             }
-            return uidList.get(uid);
         }
+
+        return mUsbPermissionManager.hasPermission(device, uid);
     }
 
     public boolean hasPermission(UsbAccessory accessory) {
-        synchronized (mLock) {
-            int uid = Binder.getCallingUid();
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                return false;
-            }
-            return uidList.get(uid);
-        }
+        return mUsbPermissionManager.hasPermission(accessory);
     }
 
     public void checkPermission(UsbDevice device, String packageName, int uid) {
@@ -213,7 +184,11 @@
         }
     }
 
-    private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+    private void requestPermissionDialog(@Nullable UsbDevice device,
+                                         @Nullable UsbAccessory accessory,
+                                         boolean canBeDefault,
+                                         String packageName,
+                                         PendingIntent pi) {
         final int uid = Binder.getCallingUid();
 
         // compare uid with packageName to foil apps pretending to be someone else
@@ -227,27 +202,15 @@
             throw new IllegalArgumentException("package " + packageName + " not found");
         }
 
-        long identity = Binder.clearCallingIdentity();
-        intent.setClassName("com.android.systemui",
-                "com.android.systemui.usb.UsbPermissionActivity");
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(Intent.EXTRA_INTENT, pi);
-        intent.putExtra("package", packageName);
-        intent.putExtra(Intent.EXTRA_UID, uid);
-        try {
-            mUserContext.startActivityAsUser(intent, mUser);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(TAG, "unable to start UsbPermissionActivity");
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        mUsbPermissionManager.requestPermissionDialog(device,
+                accessory, canBeDefault, packageName, uid, mUserContext, pi);
     }
 
     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
-      Intent intent = new Intent();
+        Intent intent = new Intent();
 
         // respond immediately if permission has already been granted
-      if (hasPermission(device, packageName, uid)) {
+        if (hasPermission(device, packageName, uid)) {
             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
             try {
@@ -270,16 +233,13 @@
             }
         }
 
-        // start UsbPermissionActivity so user can choose an activity
-        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        requestPermissionDialog(intent, packageName, pi);
+        requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi);
     }
 
     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
-        Intent intent = new Intent();
-
         // respond immediately if permission has already been granted
         if (hasPermission(accessory)) {
+            Intent intent = new Intent();
             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
             try {
@@ -290,31 +250,16 @@
             return;
         }
 
-        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        requestPermissionDialog(intent, packageName, pi);
+        requestPermissionDialog(null, accessory,
+                canBeDefault(accessory, packageName), packageName, pi);
     }
 
     public void grantDevicePermission(UsbDevice device, int uid) {
-        synchronized (mLock) {
-            String deviceName = device.getDeviceName();
-            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mDevicePermissionMap.put(deviceName, uidList);
-            }
-            uidList.put(uid, true);
-        }
+        mUsbPermissionManager.grantDevicePermission(device, uid);
     }
 
     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
-        synchronized (mLock) {
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mAccessoryPermissionMap.put(accessory, uidList);
-            }
-            uidList.put(uid, true);
-        }
+        mUsbPermissionManager.grantAccessoryPermission(accessory, uid);
     }
 
     /**
@@ -329,42 +274,108 @@
                 mUser.getIdentifier());
     }
 
+    /**
+     * Can the app be the default for the USB device. I.e. can the app be launched by default if
+     * the device is plugged in.
+     *
+     * @param device The device the app would be default for
+     * @param packageName The package name of the app
+     *
+     * @return {@code true} if the app can be default
+     */
+    private boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
+        ActivityInfo[] activities = getPackageActivities(packageName);
+        if (activities != null) {
+            int numActivities = activities.length;
+            for (int i = 0; i < numActivities; i++) {
+                ActivityInfo activityInfo = activities[i];
+
+                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                    if (parser == null) {
+                        continue;
+                    }
+
+                    XmlUtils.nextElement(parser);
+                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if ("usb-device".equals(parser.getName())) {
+                            DeviceFilter filter = DeviceFilter.read(parser);
+                            if (filter.matches(device)) {
+                                return true;
+                            }
+                        }
+
+                        XmlUtils.nextElement(parser);
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+     * the accessory is plugged in.
+     *
+     * @param accessory The accessory the app would be default for
+     * @param packageName The package name of the app
+     *
+     * @return {@code true} if the app can be default
+     */
+    private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
+        ActivityInfo[] activities = getPackageActivities(packageName);
+        if (activities != null) {
+            int numActivities = activities.length;
+            for (int i = 0; i < numActivities; i++) {
+                ActivityInfo activityInfo = activities[i];
+
+                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+                    if (parser == null) {
+                        continue;
+                    }
+
+                    XmlUtils.nextElement(parser);
+                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if ("usb-accessory".equals(parser.getName())) {
+                            AccessoryFilter filter = AccessoryFilter.read(parser);
+                            if (filter.matches(accessory)) {
+                                return true;
+                            }
+                        }
+
+                        XmlUtils.nextElement(parser);
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private ActivityInfo[] getPackageActivities(String packageName) {
+        try {
+            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+                    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+            return packageInfo.activities;
+        } catch (PackageManager.NameNotFoundException e) {
+            // ignore
+        }
+        return null;
+    }
+
     public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
         long token = dump.start(idName, id);
 
         synchronized (mLock) {
             dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
 
-            for (String deviceName : mDevicePermissionMap.keySet()) {
-                long devicePermissionToken = dump.start("device_permissions",
-                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
-
-                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
-
-                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
-                }
-
-                dump.end(devicePermissionToken);
-            }
-            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
-                long accessoryPermissionToken = dump.start("accessory_permissions",
-                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
-
-                dump.write("accessory_description",
-                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
-                        accessory.getDescription());
-
-                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
-                }
-
-                dump.end(accessoryPermissionToken);
-            }
+            mUsbPermissionManager.dump(dump);
 
             List<ResolveInfo> deviceAttachedActivities = queryIntentActivities(
                     new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED));
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 7c99c49..1426383 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -48,7 +48,7 @@
     private MessageDigest mDigester;
 
     public WifiAwareAgentNetworkSpecifier() {
-        // do nothing, already initialized to empty
+        initialize();
     }
 
     public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier ns) {
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 0515e06..657e5a7 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -18,6 +18,7 @@
 
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
@@ -60,6 +61,13 @@
                 WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR);
 
         assertEquals(dut, rereadDut);
+
+        // Ensure that individual network specifiers are satisfied by both the original & marshaled
+        // |WifiAwareNetworkAgentSpecifier instances.
+        for (WifiAwareNetworkSpecifier ns : nsSet) {
+            assertTrue(dut.satisfiesAwareNetworkSpecifier(ns));
+            assertTrue(rereadDut.satisfiesAwareNetworkSpecifier(ns));
+        }
     }
 
     /**