Merge "Update language to comply with Android's inclusive language guidance"
diff --git a/Android.bp b/Android.bp
index 8e2f0ec..c303f54 100644
--- a/Android.bp
+++ b/Android.bp
@@ -718,7 +718,6 @@
         "core/java/com/android/internal/util/TrafficStatsConstants.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "core/java/com/android/internal/util/TokenBucket.java",
-        "core/java/android/net/shared/*.java",
     ],
 }
 
@@ -728,7 +727,6 @@
     name: "framework-wifi-util-lib",
     sdk_version: "module_current",
     srcs: [
-        "core/java/android/net/shared/Inet4AddressUtils.java",
         "core/java/com/android/internal/util/Preconditions.java",
     ],
     libs: [
@@ -745,7 +743,6 @@
     name: "framework-services-net-module-wifi-shared-srcs",
     srcs: [
         "core/java/android/net/DhcpResults.java",
-        "core/java/android/net/shared/InetAddressUtils.java",
         "core/java/android/net/util/IpUtils.java",
         "core/java/android/util/LocalLog.java",
     ],
@@ -762,7 +759,6 @@
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/TrafficStatsConstants.java",
-        "core/java/android/net/shared/Inet4AddressUtils.java",
     ],
 }
 
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index cacc950..5bc10ec 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -56,6 +56,7 @@
   }
 
   public interface Parcelable {
+    method public default int getStability();
     field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
     field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7745677..b8de3f0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -365,6 +365,7 @@
         NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(log_from_module) = "network_stack"];
         NetworkValidationReported network_validation_reported = 292 [(log_from_module) = "network_stack"];
         NetworkStackQuirkReported network_stack_quirk_reported = 293 [(log_from_module) = "network_stack"];
+        KeystoreKeyEventReported keystore_key_event_reported = 302;
         NetworkTetheringReported  network_tethering_reported = 303 [(log_from_module) =  "network_tethering"];
     }
 
@@ -7903,3 +7904,111 @@
     // See android.telephony.TelephonyManager.NetworkTypeBitMask.
     optional int64 network_type_bitmask = 1;
 }
+
+/**
+ * Logs: i) creation of different types of cryptographic keys in the keystore,
+ * ii) operations performed using the keys,
+ * iii) attestation of the keys
+ * Logged from: system/security/keystore/key_event_log_handler.cpp
+ */
+message KeystoreKeyEventReported {
+
+    enum Algorithm {
+        /** Asymmetric algorithms. */
+        RSA = 1;
+        // 2 removed, do not reuse.
+        EC = 3;
+        /** Block cipher algorithms */
+        AES = 32;
+        TRIPLE_DES = 33;
+        /** MAC algorithms */
+        HMAC = 128;
+    };
+    /** Algorithm associated with the key */
+    optional Algorithm algorithm = 1;
+
+    /** Size of the key */
+    optional int32 key_size = 2;
+
+    enum KeyOrigin {
+        /** Generated in keymaster.  Should not exist outside the TEE. */
+        GENERATED = 0;
+        /** Derived inside keymaster.  Likely exists off-device. */
+        DERIVED = 1;
+        /** Imported into keymaster.  Existed as cleartext in Android. */
+        IMPORTED = 2;
+        /** Keymaster did not record origin. */
+        UNKNOWN = 3;
+        /** Securely imported into Keymaster. */
+        SECURELY_IMPORTED = 4;
+    };
+    /* Logs whether the key was generated, imported, securely imported, or derived.*/
+    optional KeyOrigin key_origin = 3;
+
+    enum HardwareAuthenticatorType {
+        NONE = 0;
+        PASSWORD = 1;
+        FINGERPRINT = 2;
+        // Additional entries must be powers of 2.
+    };
+    /**
+     * What auth types does this key require? If none,
+     * then no auth required.
+     */
+    optional HardwareAuthenticatorType user_auth_type = 4;
+
+    /**
+     * If user authentication is required, is the requirement time based? If it
+     * is not time based then this field will not be used and the key is per
+     * operation. Per operation keys must be user authenticated on each usage.
+     */
+    optional int32 user_auth_key_timeout_secs = 5;
+
+    /**
+     * padding mode, digest, block_mode and purpose should ideally be repeated
+     * fields. However, since statsd does not support repeated fields in
+     * pushed atoms, they are represented using bitmaps.
+     */
+
+    /** Track which padding mode is being used.*/
+    optional int32 padding_mode_bitmap = 6;
+
+    /** Track which digest is being used. */
+    optional int32 digest_bitmap = 7;
+
+    /** Track what block mode is being used (for encryption). */
+    optional int32 block_mode_bitmap = 8;
+
+    /** Track what purpose is this key serving. */
+    optional int32 purpose_bitmap = 9;
+
+    enum EcCurve {
+        P_224 = 0;
+        P_256 = 1;
+        P_384 = 2;
+        P_521 = 3;
+    };
+    /** Which ec curve was selected if elliptic curve cryptography is in use **/
+    optional EcCurve ec_curve = 10;
+
+    enum KeyBlobUsageRequirements {
+        STANDALONE = 0;
+        REQUIRES_FILE_SYSTEM = 1;
+    };
+    /** Standalone or is a file system required */
+    optional KeyBlobUsageRequirements key_blob_usage_reqs = 11;
+
+    enum Type {
+        KEY_OPERATION = 0;
+        KEY_CREATION = 1;
+        KEY_ATTESTATION = 2;
+    }
+    /** Key creation event, operation event or attestation event? */
+    optional Type type = 12;
+
+    /** Was the key creation, operation, or attestation successful? */
+    optional bool was_successful = 13;
+
+    /** Response code or error code */
+    optional int32 error_code = 14;
+}
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 5ab0354..1ef4f17 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -18,12 +18,13 @@
 
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.net.shared.InetAddressUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.net.module.util.InetAddressUtils;
+
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 1e004e4..1e5b6d5 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,13 +21,14 @@
 
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.net.shared.Inet4AddressUtils;
 import android.os.Build;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.net.module.util.Inet4AddressUtils;
+
 import java.io.FileDescriptor;
 import java.math.BigInteger;
 import java.net.Inet4Address;
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index f24a9bd..a973455 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -21,11 +21,11 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.net.shared.InetAddressUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
+import com.android.net.module.util.InetAddressUtils;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
diff --git a/core/java/android/net/shared/Inet4AddressUtils.java b/core/java/android/net/shared/Inet4AddressUtils.java
deleted file mode 100644
index bec0c84..0000000
--- a/core/java/android/net/shared/Inet4AddressUtils.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2019 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.net.shared;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * Collection of utilities to work with IPv4 addresses.
- * @hide
- */
-public class Inet4AddressUtils {
-
-    /**
-     * Convert a IPv4 address from an integer to an InetAddress (0x04030201 -> 1.2.3.4)
-     *
-     * <p>This method uses the higher-order int bytes as the lower-order IPv4 address bytes,
-     * which is an unusual convention. Consider {@link #intToInet4AddressHTH(int)} instead.
-     * @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
-     *                    lower-order IPv4 address byte
-     */
-    public static Inet4Address intToInet4AddressHTL(int hostAddress) {
-        return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
-    }
-
-    /**
-     * Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
-     * @param hostAddress an int coding for an IPv4 address
-     */
-    public static Inet4Address intToInet4AddressHTH(int hostAddress) {
-        byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
-                (byte) (0xff & (hostAddress >> 16)),
-                (byte) (0xff & (hostAddress >> 8)),
-                (byte) (0xff & hostAddress) };
-
-        try {
-            return (Inet4Address) InetAddress.getByAddress(addressBytes);
-        } catch (UnknownHostException e) {
-            throw new AssertionError();
-        }
-    }
-
-    /**
-     * Convert an IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x01020304)
-     *
-     * <p>This conversion can help order IP addresses: considering the ordering
-     * 192.0.2.1 < 192.0.2.2 < ..., resulting ints will follow that ordering if read as unsigned
-     * integers with {@link Integer#toUnsignedLong}.
-     * @param inetAddr is an InetAddress corresponding to the IPv4 address
-     * @return the IP address as integer
-     */
-    public static int inet4AddressToIntHTH(Inet4Address inetAddr)
-            throws IllegalArgumentException {
-        byte [] addr = inetAddr.getAddress();
-        return ((addr[0] & 0xff) << 24) | ((addr[1] & 0xff) << 16)
-                | ((addr[2] & 0xff) << 8) | (addr[3] & 0xff);
-    }
-
-    /**
-     * Convert a IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x04030201)
-     *
-     * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
-     * which is an unusual convention. Consider {@link #inet4AddressToIntHTH(Inet4Address)} instead.
-     * @param inetAddr is an InetAddress corresponding to the IPv4 address
-     * @return the IP address as integer
-     */
-    public static int inet4AddressToIntHTL(Inet4Address inetAddr) {
-        return Integer.reverseBytes(inet4AddressToIntHTH(inetAddr));
-    }
-
-    /**
-     * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0xffff8000)
-     * @return the IPv4 netmask as an integer
-     */
-    public static int prefixLengthToV4NetmaskIntHTH(int prefixLength)
-            throws IllegalArgumentException {
-        if (prefixLength < 0 || prefixLength > 32) {
-            throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
-        }
-        // (int)a << b is equivalent to a << (b & 0x1f): can't shift by 32 (-1 << 32 == -1)
-        return prefixLength == 0 ? 0 : 0xffffffff << (32 - prefixLength);
-    }
-
-    /**
-     * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0x0080ffff).
-     *
-     * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
-     * which is an unusual convention. Consider {@link #prefixLengthToV4NetmaskIntHTH(int)} instead.
-     * @return the IPv4 netmask as an integer
-     */
-    public static int prefixLengthToV4NetmaskIntHTL(int prefixLength)
-            throws IllegalArgumentException {
-        return Integer.reverseBytes(prefixLengthToV4NetmaskIntHTH(prefixLength));
-    }
-
-    /**
-     * Convert an IPv4 netmask to a prefix length, checking that the netmask is contiguous.
-     * @param netmask as a {@code Inet4Address}.
-     * @return the network prefix length
-     * @throws IllegalArgumentException the specified netmask was not contiguous.
-     * @hide
-     */
-    public static int netmaskToPrefixLength(Inet4Address netmask) {
-        // inetAddressToInt returns an int in *network* byte order.
-        int i = inet4AddressToIntHTH(netmask);
-        int prefixLength = Integer.bitCount(i);
-        int trailingZeros = Integer.numberOfTrailingZeros(i);
-        if (trailingZeros != 32 - prefixLength) {
-            throw new IllegalArgumentException("Non-contiguous netmask: " + Integer.toHexString(i));
-        }
-        return prefixLength;
-    }
-
-    /**
-     * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
-     */
-    public static int getImplicitNetmask(Inet4Address address) {
-        int firstByte = address.getAddress()[0] & 0xff;  // Convert to an unsigned value.
-        if (firstByte < 128) {
-            return 8;
-        } else if (firstByte < 192) {
-            return 16;
-        } else if (firstByte < 224) {
-            return 24;
-        } else {
-            return 32;  // Will likely not end well for other reasons.
-        }
-    }
-
-    /**
-     * Get the broadcast address for a given prefix.
-     *
-     * <p>For example 192.168.0.1/24 -> 192.168.0.255
-     */
-    public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
-            throws IllegalArgumentException {
-        final int intBroadcastAddr = inet4AddressToIntHTH(addr)
-                | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
-        return intToInet4AddressHTH(intBroadcastAddr);
-    }
-
-    /**
-     * Get a prefix mask as Inet4Address for a given prefix length.
-     *
-     * <p>For example 20 -> 255.255.240.0
-     */
-    public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
-            throws IllegalArgumentException {
-        return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
-    }
-}
diff --git a/core/java/android/net/shared/InetAddressUtils.java b/core/java/android/net/shared/InetAddressUtils.java
deleted file mode 100644
index c9ee3a7..0000000
--- a/core/java/android/net/shared/InetAddressUtils.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 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.net.shared;
-
-import android.os.Parcel;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * Collection of utilities to interact with {@link InetAddress}
- * @hide
- */
-public class InetAddressUtils {
-
-    /**
-     * Writes an InetAddress to a parcel. The address may be null. This is likely faster than
-     * calling writeSerializable.
-     * @hide
-     */
-    public static void parcelInetAddress(Parcel parcel, InetAddress address, int flags) {
-        byte[] addressArray = (address != null) ? address.getAddress() : null;
-        parcel.writeByteArray(addressArray);
-    }
-
-    /**
-     * Reads an InetAddress from a parcel. Returns null if the address that was written was null
-     * or if the data is invalid.
-     * @hide
-     */
-    public static InetAddress unparcelInetAddress(Parcel in) {
-        byte[] addressArray = in.createByteArray();
-        if (addressArray == null) {
-            return null;
-        }
-        try {
-            return InetAddress.getByAddress(addressArray);
-        } catch (UnknownHostException e) {
-            return null;
-        }
-    }
-
-    private InetAddressUtils() {}
-}
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 3d3759e..f14f66b 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -161,6 +161,7 @@
      * @return true if this parcelable is stable.
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     default @Stability int getStability() {
         return PARCELABLE_STABILITY_LOCAL;
     }
diff --git a/core/res/res/values-mcc310-mnc030/config.xml b/core/res/res/values-mcc310-mnc030/config.xml
deleted file mode 100644
index 26b9192..0000000
--- a/core/res/res/values-mcc310-mnc030/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-</resources>
-
diff --git a/core/res/res/values-mcc310-mnc070/config.xml b/core/res/res/values-mcc310-mnc070/config.xml
deleted file mode 100644
index 26b9192..0000000
--- a/core/res/res/values-mcc310-mnc070/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-</resources>
-
diff --git a/core/res/res/values-mcc310-mnc170/config.xml b/core/res/res/values-mcc310-mnc170/config.xml
index 12e448c..f6cddc9b 100644
--- a/core/res/res/values-mcc310-mnc170/config.xml
+++ b/core/res/res/values-mcc310-mnc170/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
diff --git a/core/res/res/values-mcc310-mnc280/config.xml b/core/res/res/values-mcc310-mnc280/config.xml
deleted file mode 100644
index 26b9192..0000000
--- a/core/res/res/values-mcc310-mnc280/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-</resources>
-
diff --git a/core/res/res/values-mcc310-mnc380/config.xml b/core/res/res/values-mcc310-mnc380/config.xml
index 12e448c..f6cddc9b 100644
--- a/core/res/res/values-mcc310-mnc380/config.xml
+++ b/core/res/res/values-mcc310-mnc380/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 22b8fef..6943bd4 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -49,9 +49,6 @@
         <item>"#9"</item>
     </string-array>
 
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
diff --git a/core/res/res/values-mcc310-mnc560/config.xml b/core/res/res/values-mcc310-mnc560/config.xml
index 12e448c..f6cddc9b 100644
--- a/core/res/res/values-mcc310-mnc560/config.xml
+++ b/core/res/res/values-mcc310-mnc560/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
diff --git a/core/res/res/values-mcc310-mnc950/config.xml b/core/res/res/values-mcc310-mnc950/config.xml
deleted file mode 100644
index 26b9192..0000000
--- a/core/res/res/values-mcc310-mnc950/config.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-</resources>
-
diff --git a/core/res/res/values-mcc311-mnc180/config.xml b/core/res/res/values-mcc311-mnc180/config.xml
index 12e448c..f6cddc9b 100644
--- a/core/res/res/values-mcc311-mnc180/config.xml
+++ b/core/res/res/values-mcc311-mnc180/config.xml
@@ -20,9 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 336e30e..db2f8d0 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -40,7 +40,4 @@
 
     <bool name="config_use_sim_language_file">true</bool>
 
-    <!-- Enable 5 bar signal strength icon -->
-    <bool name="config_inflateSignalStrength">true</bool>
-
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d07dd8d..c7c3ea7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4351,10 +4351,6 @@
     only. The component must be part of a system app. -->
     <string name="config_defaultSupervisionProfileOwnerComponent" translatable="false"></string>
 
-    <!-- Whether to artificially interpret all signal strengths as
-         one bar higher than they actually are -->
-    <bool name="config_inflateSignalStrength">false</bool>
-
     <!-- Contains a blacklist of apps that should not get pre-installed carrier app permission
          grants, even if the UICC claims that the app should be privileged. See b/138150105 -->
     <string-array name="config_restrictedPreinstalledCarrierApps" translatable="false"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6ee9658..7bd18c3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3860,7 +3860,6 @@
   <java-symbol type="color" name="decor_view_status_guard_light" />
 
   <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
-  <java-symbol type="bool" name="config_inflateSignalStrength" />
   <java-symbol type="array" name="config_restrictedPreinstalledCarrierApps" />
 
   <java-symbol type="array" name="config_highRefreshRateBlacklist" />
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index eeb7269..caad45b 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -345,10 +345,10 @@
 # key 395 "KEY_LIST"
 # key 396 "KEY_MEMO"
 key 397   CALENDAR
-# key 398 "KEY_RED"
-# key 399 "KEY_GREEN"
-# key 400 "KEY_YELLOW"
-# key 401 "KEY_BLUE"
+key 398   PROG_RED
+key 399   PROG_GREEN
+key 400   PROG_YELLOW
+key 401   PROG_BLUE
 key 402   CHANNEL_UP
 key 403   CHANNEL_DOWN
 # key 404 "KEY_FIRST"
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index 936ba5c..c232d13 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -1,6 +1,7 @@
+alecmouri@google.com
+djsollen@google.com
 jreck@google.com
 njawad@google.com
-djsollen@google.com
-stani@google.com
-scroggo@google.com
 reed@google.com
+scroggo@google.com
+stani@google.com
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index fd4371c..7942806 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -16,6 +16,9 @@
     name: "libinputservice",
     srcs: [
         "PointerController.cpp",
+        "PointerControllerContext.cpp",
+        "MouseCursorController.cpp",
+        "TouchSpotController.cpp",
         "SpriteController.cpp",
         "SpriteIcon.cpp",
     ],
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
new file mode 100644
index 0000000..80b555b
--- /dev/null
+++ b/libs/input/MouseCursorController.cpp
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "MouseCursorController"
+//#define LOG_NDEBUG 0
+
+// Log debug messages about pointer updates
+#define DEBUG_MOUSE_CURSOR_UPDATES 0
+
+#include "MouseCursorController.h"
+
+#include <log/log.h>
+
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+
+namespace {
+// Time to spend fading out the pointer completely.
+const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
+} // namespace
+
+namespace android {
+
+// --- MouseCursorController ---
+
+MouseCursorController::MouseCursorController(PointerControllerContext& context)
+      : mContext(context) {
+    std::scoped_lock lock(mLock);
+
+    mLocked.animationFrameIndex = 0;
+    mLocked.lastFrameUpdatedTime = 0;
+
+    mLocked.pointerFadeDirection = 0;
+    mLocked.pointerX = 0;
+    mLocked.pointerY = 0;
+    mLocked.pointerAlpha = 0.0f; // pointer is initially faded
+    mLocked.pointerSprite = mContext.getSpriteController()->createSprite();
+    mLocked.updatePointerIcon = false;
+    mLocked.requestedPointerType = mContext.getPolicy()->getDefaultPointerIconId();
+
+    mLocked.resourcesLoaded = false;
+
+    mLocked.buttonState = 0;
+}
+
+MouseCursorController::~MouseCursorController() {
+    std::scoped_lock lock(mLock);
+
+    mLocked.pointerSprite.clear();
+}
+
+bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+                                      float* outMaxY) const {
+    std::scoped_lock lock(mLock);
+
+    return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
+}
+
+bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX,
+                                            float* outMaxY) const REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return false;
+    }
+
+    *outMinX = mLocked.viewport.logicalLeft;
+    *outMinY = mLocked.viewport.logicalTop;
+    *outMaxX = mLocked.viewport.logicalRight - 1;
+    *outMaxY = mLocked.viewport.logicalBottom - 1;
+    return true;
+}
+
+void MouseCursorController::move(float deltaX, float deltaY) {
+#if DEBUG_MOUSE_CURSOR_UPDATES
+    ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
+#endif
+    if (deltaX == 0.0f && deltaY == 0.0f) {
+        return;
+    }
+
+    std::scoped_lock lock(mLock);
+
+    setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
+}
+
+void MouseCursorController::setButtonState(int32_t buttonState) {
+#if DEBUG_MOUSE_CURSOR_UPDATES
+    ALOGD("Set button state 0x%08x", buttonState);
+#endif
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.buttonState != buttonState) {
+        mLocked.buttonState = buttonState;
+    }
+}
+
+int32_t MouseCursorController::getButtonState() const {
+    std::scoped_lock lock(mLock);
+    return mLocked.buttonState;
+}
+
+void MouseCursorController::setPosition(float x, float y) {
+#if DEBUG_MOUSE_CURSOR_UPDATES
+    ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
+#endif
+    std::scoped_lock lock(mLock);
+    setPositionLocked(x, y);
+}
+
+void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
+    float minX, minY, maxX, maxY;
+    if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
+        if (x <= minX) {
+            mLocked.pointerX = minX;
+        } else if (x >= maxX) {
+            mLocked.pointerX = maxX;
+        } else {
+            mLocked.pointerX = x;
+        }
+        if (y <= minY) {
+            mLocked.pointerY = minY;
+        } else if (y >= maxY) {
+            mLocked.pointerY = maxY;
+        } else {
+            mLocked.pointerY = y;
+        }
+        updatePointerLocked();
+    }
+}
+
+void MouseCursorController::getPosition(float* outX, float* outY) const {
+    std::scoped_lock lock(mLock);
+
+    *outX = mLocked.pointerX;
+    *outY = mLocked.pointerY;
+}
+
+int32_t MouseCursorController::getDisplayId() const {
+    std::scoped_lock lock(mLock);
+    return mLocked.viewport.displayId;
+}
+
+void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
+    std::scoped_lock lock(mLock);
+
+    // Remove the inactivity timeout, since we are fading now.
+    mContext.removeInactivityTimeout();
+
+    // Start fading.
+    if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
+        mLocked.pointerFadeDirection = 0;
+        mLocked.pointerAlpha = 0.0f;
+        updatePointerLocked();
+    } else {
+        mLocked.pointerFadeDirection = -1;
+        mContext.startAnimation();
+    }
+}
+
+void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
+    std::scoped_lock lock(mLock);
+
+    // Always reset the inactivity timer.
+    mContext.resetInactivityTimeout();
+
+    // Start unfading.
+    if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
+        mLocked.pointerFadeDirection = 0;
+        mLocked.pointerAlpha = 1.0f;
+        updatePointerLocked();
+    } else {
+        mLocked.pointerFadeDirection = 1;
+        mContext.startAnimation();
+    }
+}
+
+void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
+    std::scoped_lock lock(mLock);
+
+    loadResourcesLocked(getAdditionalMouseResources);
+    updatePointerLocked();
+}
+
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+    width = viewport.deviceWidth;
+    height = viewport.deviceHeight;
+
+    if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
+        viewport.orientation == DISPLAY_ORIENTATION_270) {
+        std::swap(width, height);
+    }
+}
+
+void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
+                                               bool getAdditionalMouseResources) {
+    std::scoped_lock lock(mLock);
+
+    if (viewport == mLocked.viewport) {
+        return;
+    }
+
+    const DisplayViewport oldViewport = mLocked.viewport;
+    mLocked.viewport = viewport;
+
+    int32_t oldDisplayWidth, oldDisplayHeight;
+    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+    int32_t newDisplayWidth, newDisplayHeight;
+    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+    // Reset cursor position to center if size or display changed.
+    if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
+        oldDisplayHeight != newDisplayHeight) {
+        float minX, minY, maxX, maxY;
+        if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
+            mLocked.pointerX = (minX + maxX) * 0.5f;
+            mLocked.pointerY = (minY + maxY) * 0.5f;
+            // Reload icon resources for density may be changed.
+            loadResourcesLocked(getAdditionalMouseResources);
+        } else {
+            mLocked.pointerX = 0;
+            mLocked.pointerY = 0;
+        }
+    } else if (oldViewport.orientation != viewport.orientation) {
+        // Apply offsets to convert from the pixel top-left corner position to the pixel center.
+        // This creates an invariant frame of reference that we can easily rotate when
+        // taking into account that the pointer may be located at fractional pixel offsets.
+        float x = mLocked.pointerX + 0.5f;
+        float y = mLocked.pointerY + 0.5f;
+        float temp;
+
+        // Undo the previous rotation.
+        switch (oldViewport.orientation) {
+            case DISPLAY_ORIENTATION_90:
+                temp = x;
+                x = oldViewport.deviceHeight - y;
+                y = temp;
+                break;
+            case DISPLAY_ORIENTATION_180:
+                x = oldViewport.deviceWidth - x;
+                y = oldViewport.deviceHeight - y;
+                break;
+            case DISPLAY_ORIENTATION_270:
+                temp = x;
+                x = y;
+                y = oldViewport.deviceWidth - temp;
+                break;
+        }
+
+        // Perform the new rotation.
+        switch (viewport.orientation) {
+            case DISPLAY_ORIENTATION_90:
+                temp = x;
+                x = y;
+                y = viewport.deviceHeight - temp;
+                break;
+            case DISPLAY_ORIENTATION_180:
+                x = viewport.deviceWidth - x;
+                y = viewport.deviceHeight - y;
+                break;
+            case DISPLAY_ORIENTATION_270:
+                temp = x;
+                x = viewport.deviceWidth - y;
+                y = temp;
+                break;
+        }
+
+        // Apply offsets to convert from the pixel center to the pixel top-left corner position
+        // and save the results.
+        mLocked.pointerX = x - 0.5f;
+        mLocked.pointerY = y - 0.5f;
+    }
+
+    updatePointerLocked();
+}
+
+void MouseCursorController::updatePointerIcon(int32_t iconId) {
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.requestedPointerType != iconId) {
+        mLocked.requestedPointerType = iconId;
+        mLocked.updatePointerIcon = true;
+        updatePointerLocked();
+    }
+}
+
+void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
+    std::scoped_lock lock(mLock);
+
+    const int32_t iconId = mContext.getPolicy()->getCustomPointerIconId();
+    mLocked.additionalMouseResources[iconId] = icon;
+    mLocked.requestedPointerType = iconId;
+    mLocked.updatePointerIcon = true;
+    updatePointerLocked();
+}
+
+bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) {
+    nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
+
+    std::scoped_lock lock(mLock);
+
+    // Animate pointer fade.
+    if (mLocked.pointerFadeDirection < 0) {
+        mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
+        if (mLocked.pointerAlpha <= 0.0f) {
+            mLocked.pointerAlpha = 0.0f;
+            mLocked.pointerFadeDirection = 0;
+        } else {
+            keepAnimating = true;
+        }
+        updatePointerLocked();
+    } else if (mLocked.pointerFadeDirection > 0) {
+        mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
+        if (mLocked.pointerAlpha >= 1.0f) {
+            mLocked.pointerAlpha = 1.0f;
+            mLocked.pointerFadeDirection = 0;
+        } else {
+            keepAnimating = true;
+        }
+        updatePointerLocked();
+    }
+
+    return keepAnimating;
+}
+
+bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) {
+    std::scoped_lock lock(mLock);
+
+    std::map<int32_t, PointerAnimation>::const_iterator iter =
+            mLocked.animationResources.find(mLocked.requestedPointerType);
+    if (iter == mLocked.animationResources.end()) {
+        return false;
+    }
+
+    if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
+        sp<SpriteController> spriteController = mContext.getSpriteController();
+        spriteController->openTransaction();
+
+        int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
+        mLocked.animationFrameIndex += incr;
+        mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
+        while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
+            mLocked.animationFrameIndex -= iter->second.animationFrames.size();
+        }
+        mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
+
+        spriteController->closeTransaction();
+    }
+
+    // Keep animating.
+    return true;
+}
+
+void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+    sp<SpriteController> spriteController = mContext.getSpriteController();
+    spriteController->openTransaction();
+
+    mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
+    mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
+
+    if (mLocked.pointerAlpha > 0) {
+        mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
+        mLocked.pointerSprite->setVisible(true);
+    } else {
+        mLocked.pointerSprite->setVisible(false);
+    }
+
+    if (mLocked.updatePointerIcon) {
+        if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) {
+            mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+        } else {
+            std::map<int32_t, SpriteIcon>::const_iterator iter =
+                    mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
+            if (iter != mLocked.additionalMouseResources.end()) {
+                std::map<int32_t, PointerAnimation>::const_iterator anim_iter =
+                        mLocked.animationResources.find(mLocked.requestedPointerType);
+                if (anim_iter != mLocked.animationResources.end()) {
+                    mLocked.animationFrameIndex = 0;
+                    mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+                    mContext.startAnimation();
+                }
+                mLocked.pointerSprite->setIcon(iter->second);
+            } else {
+                ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType);
+                mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+            }
+        }
+        mLocked.updatePointerIcon = false;
+    }
+
+    spriteController->closeTransaction();
+}
+
+void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
+    if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
+
+    sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
+    policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
+    policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
+
+    mLocked.additionalMouseResources.clear();
+    mLocked.animationResources.clear();
+    if (getAdditionalMouseResources) {
+        policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                             &mLocked.animationResources,
+                                             mLocked.viewport.displayId);
+    }
+
+    mLocked.updatePointerIcon = true;
+}
+
+bool MouseCursorController::isViewportValid() {
+    std::scoped_lock lock(mLock);
+    return mLocked.viewport.isValid();
+}
+
+void MouseCursorController::getAdditionalMouseResources() {
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.additionalMouseResources.empty()) {
+        mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                                           &mLocked.animationResources,
+                                                           mLocked.viewport.displayId);
+    }
+    mLocked.updatePointerIcon = true;
+    updatePointerLocked();
+}
+
+bool MouseCursorController::resourcesLoaded() {
+    std::scoped_lock lock(mLock);
+    return mLocked.resourcesLoaded;
+}
+
+} // namespace android
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
new file mode 100644
index 0000000..448165b
--- /dev/null
+++ b/libs/input/MouseCursorController.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _UI_MOUSE_CURSOR_CONTROLLER_H
+#define _UI_MOUSE_CURSOR_CONTROLLER_H
+
+#include <gui/DisplayEventReceiver.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <ui/DisplayInfo.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "PointerControllerContext.h"
+#include "SpriteController.h"
+
+namespace android {
+
+/*
+ * Helper class for PointerController that specifically handles
+ * mouse cursor resources and actions.
+ */
+class MouseCursorController {
+public:
+    MouseCursorController(PointerControllerContext& context);
+    ~MouseCursorController();
+
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
+    void move(float deltaX, float deltaY);
+    void setButtonState(int32_t buttonState);
+    int32_t getButtonState() const;
+    void setPosition(float x, float y);
+    void getPosition(float* outX, float* outY) const;
+    int32_t getDisplayId() const;
+    void fade(PointerControllerInterface::Transition transition);
+    void unfade(PointerControllerInterface::Transition transition);
+    void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources);
+
+    void updatePointerIcon(int32_t iconId);
+    void setCustomPointerIcon(const SpriteIcon& icon);
+    void reloadPointerResources(bool getAdditionalMouseResources);
+
+    void getAdditionalMouseResources();
+    bool isViewportValid();
+
+    bool doBitmapAnimation(nsecs_t timestamp);
+    bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating);
+
+    bool resourcesLoaded();
+
+private:
+    mutable std::mutex mLock;
+
+    PointerResources mResources;
+
+    PointerControllerContext& mContext;
+
+    struct Locked {
+        DisplayViewport viewport;
+
+        size_t animationFrameIndex;
+        nsecs_t lastFrameUpdatedTime;
+
+        int32_t pointerFadeDirection;
+        float pointerX;
+        float pointerY;
+        float pointerAlpha;
+        sp<Sprite> pointerSprite;
+        SpriteIcon pointerIcon;
+        bool updatePointerIcon;
+
+        bool resourcesLoaded;
+
+        std::map<int32_t, SpriteIcon> additionalMouseResources;
+        std::map<int32_t, PointerAnimation> animationResources;
+
+        int32_t requestedPointerType;
+
+        int32_t buttonState;
+
+    } mLocked GUARDED_BY(mLock);
+
+    bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
+    void setPositionLocked(float x, float y);
+
+    void updatePointerLocked();
+
+    void loadResourcesLocked(bool getAdditionalMouseResources);
+};
+
+} // namespace android
+
+#endif // _UI_MOUSE_CURSOR_CONTROLLER_H
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 5e480a6..14c96ce 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -21,31 +21,26 @@
 #define DEBUG_POINTER_UPDATES 0
 
 #include "PointerController.h"
+#include "MouseCursorController.h"
+#include "PointerControllerContext.h"
+#include "TouchSpotController.h"
 
 #include <log/log.h>
 
-#include <memory>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
 
 namespace android {
 
 // --- PointerController ---
 
-// Time to wait before starting the fade when the pointer is inactive.
-static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
-static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
-
-// Time to spend fading out the spot completely.
-static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
-
-// Time to spend fading out the pointer completely.
-static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
-
-// The number of events to be read at once for DisplayEventReceiver.
-static const int EVENT_BUFFER_SIZE = 100;
-
 std::shared_ptr<PointerController> PointerController::create(
         const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
         const sp<SpriteController>& spriteController) {
+    // using 'new' to access non-public constructor
     std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
             new PointerController(policy, looper, spriteController));
 
@@ -60,758 +55,175 @@
      * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
      */
 
-    controller->mHandler->pointerController = controller;
-    controller->mCallback->pointerController = controller;
-    if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) {
-        controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
-                                   Looper::EVENT_INPUT, controller->mCallback, nullptr);
-    } else {
-        ALOGE("Failed to initialize DisplayEventReceiver.");
-    }
+    controller->mContext.setHandlerController(controller);
+    controller->mContext.setCallbackController(controller);
+    controller->mContext.initializeDisplayEventReceiver();
     return controller;
 }
 
 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
                                      const sp<Looper>& looper,
                                      const sp<SpriteController>& spriteController)
-      : mPolicy(policy),
-        mLooper(looper),
-        mSpriteController(spriteController),
-        mHandler(new MessageHandler()),
-        mCallback(new LooperCallback()) {
-    AutoMutex _l(mLock);
-
-    mLocked.animationPending = false;
-
-    mLocked.presentation = Presentation::POINTER;
-    mLocked.presentationChanged = false;
-
-    mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
-
-    mLocked.pointerFadeDirection = 0;
-    mLocked.pointerX = 0;
-    mLocked.pointerY = 0;
-    mLocked.pointerAlpha = 0.0f; // pointer is initially faded
-    mLocked.pointerSprite = mSpriteController->createSprite();
-    mLocked.pointerIconChanged = false;
-    mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId();
-
-    mLocked.animationFrameIndex = 0;
-    mLocked.lastFrameUpdatedTime = 0;
-
-    mLocked.buttonState = 0;
+      : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
+    std::scoped_lock lock(mLock);
+    mLocked.presentation = Presentation::SPOT;
 }
 
-PointerController::~PointerController() {
-    mLooper->removeMessages(mHandler);
-
-    AutoMutex _l(mLock);
-
-    mLocked.pointerSprite.clear();
-
-    for (auto& it : mLocked.spotsByDisplay) {
-        const std::vector<Spot*>& spots = it.second;
-        size_t numSpots = spots.size();
-        for (size_t i = 0; i < numSpots; i++) {
-            delete spots[i];
-        }
-    }
-    mLocked.spotsByDisplay.clear();
-    mLocked.recycledSprites.clear();
-}
-
-bool PointerController::getBounds(float* outMinX, float* outMinY,
-        float* outMaxX, float* outMaxY) const {
-    AutoMutex _l(mLock);
-
-    return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
-}
-
-bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
-        float* outMaxX, float* outMaxY) const {
-
-    if (!mLocked.viewport.isValid()) {
-        return false;
-    }
-
-    *outMinX = mLocked.viewport.logicalLeft;
-    *outMinY = mLocked.viewport.logicalTop;
-    *outMaxX = mLocked.viewport.logicalRight - 1;
-    *outMaxY = mLocked.viewport.logicalBottom - 1;
-    return true;
+bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+                                  float* outMaxY) const {
+    return mCursorController.getBounds(outMinX, outMinY, outMaxX, outMaxY);
 }
 
 void PointerController::move(float deltaX, float deltaY) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
-#endif
-    if (deltaX == 0.0f && deltaY == 0.0f) {
-        return;
-    }
-
-    AutoMutex _l(mLock);
-
-    setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
+    mCursorController.move(deltaX, deltaY);
 }
 
 void PointerController::setButtonState(int32_t buttonState) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("Set button state 0x%08x", buttonState);
-#endif
-    AutoMutex _l(mLock);
-
-    if (mLocked.buttonState != buttonState) {
-        mLocked.buttonState = buttonState;
-    }
+    mCursorController.setButtonState(buttonState);
 }
 
 int32_t PointerController::getButtonState() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.buttonState;
+    return mCursorController.getButtonState();
 }
 
 void PointerController::setPosition(float x, float y) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
-#endif
-    AutoMutex _l(mLock);
-
-    setPositionLocked(x, y);
-}
-
-void PointerController::setPositionLocked(float x, float y) {
-    float minX, minY, maxX, maxY;
-    if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
-        if (x <= minX) {
-            mLocked.pointerX = minX;
-        } else if (x >= maxX) {
-            mLocked.pointerX = maxX;
-        } else {
-            mLocked.pointerX = x;
-        }
-        if (y <= minY) {
-            mLocked.pointerY = minY;
-        } else if (y >= maxY) {
-            mLocked.pointerY = maxY;
-        } else {
-            mLocked.pointerY = y;
-        }
-        updatePointerLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.setPosition(x, y);
 }
 
 void PointerController::getPosition(float* outX, float* outY) const {
-    AutoMutex _l(mLock);
-
-    *outX = mLocked.pointerX;
-    *outY = mLocked.pointerY;
+    mCursorController.getPosition(outX, outY);
 }
 
 int32_t PointerController::getDisplayId() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.viewport.displayId;
+    return mCursorController.getDisplayId();
 }
 
 void PointerController::fade(Transition transition) {
-    AutoMutex _l(mLock);
-
-    // Remove the inactivity timeout, since we are fading now.
-    removeInactivityTimeoutLocked();
-
-    // Start fading.
-    if (transition == Transition::IMMEDIATE) {
-        mLocked.pointerFadeDirection = 0;
-        mLocked.pointerAlpha = 0.0f;
-        updatePointerLocked();
-    } else {
-        mLocked.pointerFadeDirection = -1;
-        startAnimationLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.fade(transition);
 }
 
 void PointerController::unfade(Transition transition) {
-    AutoMutex _l(mLock);
-
-    // Always reset the inactivity timer.
-    resetInactivityTimeoutLocked();
-
-    // Start unfading.
-    if (transition == Transition::IMMEDIATE) {
-        mLocked.pointerFadeDirection = 0;
-        mLocked.pointerAlpha = 1.0f;
-        updatePointerLocked();
-    } else {
-        mLocked.pointerFadeDirection = 1;
-        startAnimationLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.unfade(transition);
 }
 
 void PointerController::setPresentation(Presentation presentation) {
-    AutoMutex _l(mLock);
+    std::scoped_lock lock(mLock);
 
     if (mLocked.presentation == presentation) {
         return;
     }
 
     mLocked.presentation = presentation;
-    mLocked.presentationChanged = true;
 
-    if (!mLocked.viewport.isValid()) {
+    if (!mCursorController.isViewportValid()) {
         return;
     }
 
     if (presentation == Presentation::POINTER) {
-        if (mLocked.additionalMouseResources.empty()) {
-            mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                                  &mLocked.animationResources,
-                                                  mLocked.viewport.displayId);
-        }
-        fadeOutAndReleaseAllSpotsLocked();
-        updatePointerLocked();
+        mCursorController.getAdditionalMouseResources();
+        clearSpotsLocked();
     }
 }
 
-void PointerController::setSpots(const PointerCoords* spotCoords,
-        const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("setSpots: idBits=%08x", spotIdBits.value);
-    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
-        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
-                c.getAxisValue(AMOTION_EVENT_AXIS_X),
-                c.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                displayId);
+void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                                 BitSet32 spotIdBits, int32_t displayId) {
+    std::scoped_lock lock(mLock);
+    auto it = mLocked.spotControllers.find(displayId);
+    if (it == mLocked.spotControllers.end()) {
+        mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
     }
-#endif
-
-    AutoMutex _l(mLock);
-    if (!mLocked.viewport.isValid()) {
-        return;
-    }
-
-    std::vector<Spot*> newSpots;
-    std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
-            mLocked.spotsByDisplay.find(displayId);
-    if (iter != mLocked.spotsByDisplay.end()) {
-        newSpots = iter->second;
-    }
-
-    mSpriteController->openTransaction();
-
-    // Add or move spots for fingers that are down.
-    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
-                ? mResources.spotTouch : mResources.spotHover;
-        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
-        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
-
-        Spot* spot = getSpot(id, newSpots);
-        if (!spot) {
-            spot = createAndAddSpotLocked(id, newSpots);
-        }
-
-        spot->updateSprite(&icon, x, y, displayId);
-    }
-
-    // Remove spots for fingers that went up.
-    for (size_t i = 0; i < newSpots.size(); i++) {
-        Spot* spot = newSpots[i];
-        if (spot->id != Spot::INVALID_ID
-                && !spotIdBits.hasBit(spot->id)) {
-            fadeOutAndReleaseSpotLocked(spot);
-        }
-    }
-
-    mSpriteController->closeTransaction();
-    mLocked.spotsByDisplay[displayId] = newSpots;
+    mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
 }
 
 void PointerController::clearSpots() {
-#if DEBUG_POINTER_UPDATES
-    ALOGD("clearSpots");
-#endif
+    std::scoped_lock lock(mLock);
+    clearSpotsLocked();
+}
 
-    AutoMutex _l(mLock);
-    if (!mLocked.viewport.isValid()) {
-        return;
+void PointerController::clearSpotsLocked() REQUIRES(mLock) {
+    for (auto& [displayID, spotController] : mLocked.spotControllers) {
+        spotController.clearSpots();
     }
-
-    fadeOutAndReleaseAllSpotsLocked();
 }
 
 void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
-    AutoMutex _l(mLock);
-
-    if (mLocked.inactivityTimeout != inactivityTimeout) {
-        mLocked.inactivityTimeout = inactivityTimeout;
-        resetInactivityTimeoutLocked();
-    }
+    mContext.setInactivityTimeout(inactivityTimeout);
 }
 
 void PointerController::reloadPointerResources() {
-    AutoMutex _l(mLock);
+    std::scoped_lock lock(mLock);
 
-    loadResourcesLocked();
-    updatePointerLocked();
-}
+    for (auto& [displayID, spotController] : mLocked.spotControllers) {
+        spotController.reloadSpotResources();
+    }
 
-/**
- * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
- * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
- */
-static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
-    width = viewport.deviceWidth;
-    height = viewport.deviceHeight;
-
-    if (viewport.orientation == DISPLAY_ORIENTATION_90
-            || viewport.orientation == DISPLAY_ORIENTATION_270) {
-        std::swap(width, height);
+    if (mCursorController.resourcesLoaded()) {
+        bool getAdditionalMouseResources = false;
+        if (mLocked.presentation == PointerController::Presentation::POINTER) {
+            getAdditionalMouseResources = true;
+        }
+        mCursorController.reloadPointerResources(getAdditionalMouseResources);
     }
 }
 
 void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
-    AutoMutex _l(mLock);
-    if (viewport == mLocked.viewport) {
-        return;
+    std::scoped_lock lock(mLock);
+
+    bool getAdditionalMouseResources = false;
+    if (mLocked.presentation == PointerController::Presentation::POINTER) {
+        getAdditionalMouseResources = true;
     }
-
-    const DisplayViewport oldViewport = mLocked.viewport;
-    mLocked.viewport = viewport;
-
-    int32_t oldDisplayWidth, oldDisplayHeight;
-    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
-    int32_t newDisplayWidth, newDisplayHeight;
-    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
-
-    // Reset cursor position to center if size or display changed.
-    if (oldViewport.displayId != viewport.displayId
-            || oldDisplayWidth != newDisplayWidth
-            || oldDisplayHeight != newDisplayHeight) {
-
-        float minX, minY, maxX, maxY;
-        if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
-            mLocked.pointerX = (minX + maxX) * 0.5f;
-            mLocked.pointerY = (minY + maxY) * 0.5f;
-            // Reload icon resources for density may be changed.
-            loadResourcesLocked();
-        } else {
-            mLocked.pointerX = 0;
-            mLocked.pointerY = 0;
-        }
-
-        fadeOutAndReleaseAllSpotsLocked();
-    } else if (oldViewport.orientation != viewport.orientation) {
-        // Apply offsets to convert from the pixel top-left corner position to the pixel center.
-        // This creates an invariant frame of reference that we can easily rotate when
-        // taking into account that the pointer may be located at fractional pixel offsets.
-        float x = mLocked.pointerX + 0.5f;
-        float y = mLocked.pointerY + 0.5f;
-        float temp;
-
-        // Undo the previous rotation.
-        switch (oldViewport.orientation) {
-        case DISPLAY_ORIENTATION_90:
-            temp = x;
-            x =  oldViewport.deviceHeight - y;
-            y = temp;
-            break;
-        case DISPLAY_ORIENTATION_180:
-            x = oldViewport.deviceWidth - x;
-            y = oldViewport.deviceHeight - y;
-            break;
-        case DISPLAY_ORIENTATION_270:
-            temp = x;
-            x = y;
-            y = oldViewport.deviceWidth - temp;
-            break;
-        }
-
-        // Perform the new rotation.
-        switch (viewport.orientation) {
-        case DISPLAY_ORIENTATION_90:
-            temp = x;
-            x = y;
-            y = viewport.deviceHeight - temp;
-            break;
-        case DISPLAY_ORIENTATION_180:
-            x = viewport.deviceWidth - x;
-            y = viewport.deviceHeight - y;
-            break;
-        case DISPLAY_ORIENTATION_270:
-            temp = x;
-            x = viewport.deviceWidth - y;
-            y = temp;
-            break;
-        }
-
-        // Apply offsets to convert from the pixel center to the pixel top-left corner position
-        // and save the results.
-        mLocked.pointerX = x - 0.5f;
-        mLocked.pointerY = y - 0.5f;
-    }
-
-    updatePointerLocked();
+    mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
 }
 
 void PointerController::updatePointerIcon(int32_t iconId) {
-    AutoMutex _l(mLock);
-    if (mLocked.requestedPointerType != iconId) {
-        mLocked.requestedPointerType = iconId;
-        mLocked.presentationChanged = true;
-        updatePointerLocked();
-    }
+    std::scoped_lock lock(mLock);
+    mCursorController.updatePointerIcon(iconId);
 }
 
 void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
-    AutoMutex _l(mLock);
-
-    const int32_t iconId = mPolicy->getCustomPointerIconId();
-    mLocked.additionalMouseResources[iconId] = icon;
-    mLocked.requestedPointerType = iconId;
-    mLocked.presentationChanged = true;
-
-    updatePointerLocked();
-}
-
-void PointerController::MessageHandler::handleMessage(const Message& message) {
-    std::shared_ptr<PointerController> controller = pointerController.lock();
-
-    if (controller == nullptr) {
-        ALOGE("PointerController instance was released before processing message: what=%d",
-              message.what);
-        return;
-    }
-    switch (message.what) {
-    case MSG_INACTIVITY_TIMEOUT:
-        controller->doInactivityTimeout();
-        break;
-    }
-}
-
-int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) {
-    std::shared_ptr<PointerController> controller = pointerController.lock();
-    if (controller == nullptr) {
-        ALOGW("PointerController instance was released with pending callbacks.  events=0x%x",
-              events);
-        return 0; // Remove the callback, the PointerController is gone anyways
-    }
-    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
-        ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events);
-        return 0; // remove the callback
-    }
-
-    if (!(events & Looper::EVENT_INPUT)) {
-        ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
-        return 1; // keep the callback
-    }
-
-    bool gotVsync = false;
-    ssize_t n;
-    nsecs_t timestamp;
-    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
-            if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                timestamp = buf[i].header.timestamp;
-                gotVsync = true;
-            }
-        }
-    }
-    if (gotVsync) {
-        controller->doAnimate(timestamp);
-    }
-    return 1;  // keep the callback
+    std::scoped_lock lock(mLock);
+    mCursorController.setCustomPointerIcon(icon);
 }
 
 void PointerController::doAnimate(nsecs_t timestamp) {
-    AutoMutex _l(mLock);
+    std::scoped_lock lock(mLock);
 
-    mLocked.animationPending = false;
+    mContext.setAnimationPending(false);
 
-    bool keepFading = doFadingAnimationLocked(timestamp);
-    bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp);
+    bool keepFading = false;
+    keepFading = mCursorController.doFadingAnimation(timestamp, keepFading);
+
+    for (auto& [displayID, spotController] : mLocked.spotControllers) {
+        keepFading = spotController.doFadingAnimation(timestamp, keepFading);
+    }
+
+    bool keepBitmapFlipping = mCursorController.doBitmapAnimation(timestamp);
     if (keepFading || keepBitmapFlipping) {
-        startAnimationLocked();
+        mContext.startAnimation();
     }
 }
 
-bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) {
-    bool keepAnimating = false;
-    nsecs_t frameDelay = timestamp - mLocked.animationTime;
-
-    // Animate pointer fade.
-    if (mLocked.pointerFadeDirection < 0) {
-        mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
-        if (mLocked.pointerAlpha <= 0.0f) {
-            mLocked.pointerAlpha = 0.0f;
-            mLocked.pointerFadeDirection = 0;
-        } else {
-            keepAnimating = true;
-        }
-        updatePointerLocked();
-    } else if (mLocked.pointerFadeDirection > 0) {
-        mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
-        if (mLocked.pointerAlpha >= 1.0f) {
-            mLocked.pointerAlpha = 1.0f;
-            mLocked.pointerFadeDirection = 0;
-        } else {
-            keepAnimating = true;
-        }
-        updatePointerLocked();
-    }
-
-    // Animate spots that are fading out and being removed.
-    for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) {
-        std::vector<Spot*>& spots = it->second;
-        size_t numSpots = spots.size();
-        for (size_t i = 0; i < numSpots;) {
-            Spot* spot = spots[i];
-            if (spot->id == Spot::INVALID_ID) {
-                spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
-                if (spot->alpha <= 0) {
-                    spots.erase(spots.begin() + i);
-                    releaseSpotLocked(spot);
-                    numSpots--;
-                    continue;
-                } else {
-                    spot->sprite->setAlpha(spot->alpha);
-                    keepAnimating = true;
-                }
-            }
-            ++i;
-        }
-
-        if (spots.size() == 0) {
-            it = mLocked.spotsByDisplay.erase(it);
-        } else {
-            ++it;
-        }
-    }
-
-    return keepAnimating;
-}
-
-bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) {
-    std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find(
-            mLocked.requestedPointerType);
-    if (iter == mLocked.animationResources.end()) {
-        return false;
-    }
-
-    if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
-        mSpriteController->openTransaction();
-
-        int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
-        mLocked.animationFrameIndex += incr;
-        mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
-        while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
-            mLocked.animationFrameIndex -= iter->second.animationFrames.size();
-        }
-        mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
-
-        mSpriteController->closeTransaction();
-    }
-
-    // Keep animating.
-    return true;
-}
-
 void PointerController::doInactivityTimeout() {
     fade(Transition::GRADUAL);
 }
 
-void PointerController::startAnimationLocked() {
-    if (!mLocked.animationPending) {
-        mLocked.animationPending = true;
-        mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        mDisplayEventReceiver.requestNextVsync();
-    }
-}
-
-void PointerController::resetInactivityTimeoutLocked() {
-    mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
-
-    nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
-            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
-            : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
-    mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
-}
-
-void PointerController::removeInactivityTimeoutLocked() {
-    mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
-}
-
-void PointerController::updatePointerLocked() REQUIRES(mLock) {
-    if (!mLocked.viewport.isValid()) {
-        return;
+void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
+    std::unordered_set<int32_t> displayIdSet;
+    for (DisplayViewport viewport : viewports) {
+        displayIdSet.insert(viewport.displayId);
     }
 
-    mSpriteController->openTransaction();
-
-    mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
-    mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
-    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
-
-    if (mLocked.pointerAlpha > 0) {
-        mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
-        mLocked.pointerSprite->setVisible(true);
-    } else {
-        mLocked.pointerSprite->setVisible(false);
-    }
-
-    if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
-        if (mLocked.presentation == Presentation::POINTER) {
-            if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
-                mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
-            } else {
-                std::map<int32_t, SpriteIcon>::const_iterator iter =
-                    mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
-                if (iter != mLocked.additionalMouseResources.end()) {
-                    std::map<int32_t, PointerAnimation>::const_iterator anim_iter =
-                            mLocked.animationResources.find(mLocked.requestedPointerType);
-                    if (anim_iter != mLocked.animationResources.end()) {
-                        mLocked.animationFrameIndex = 0;
-                        mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        startAnimationLocked();
-                    }
-                    mLocked.pointerSprite->setIcon(iter->second);
-                } else {
-                    ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType);
-                    mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
-                }
-            }
+    std::scoped_lock lock(mLock);
+    for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
+        int32_t displayID = it->first;
+        if (!displayIdSet.count(displayID)) {
+            it = mLocked.spotControllers.erase(it);
         } else {
-            mLocked.pointerSprite->setIcon(mResources.spotAnchor);
-        }
-        mLocked.pointerIconChanged = false;
-        mLocked.presentationChanged = false;
-    }
-
-    mSpriteController->closeTransaction();
-}
-
-PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) {
-    for (size_t i = 0; i < spots.size(); i++) {
-        Spot* spot = spots[i];
-        if (spot->id == id) {
-            return spot;
-        }
-    }
-
-    return nullptr;
-}
-
-PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id,
-        std::vector<Spot*>& spots) {
-    // Remove spots until we have fewer than MAX_SPOTS remaining.
-    while (spots.size() >= MAX_SPOTS) {
-        Spot* spot = removeFirstFadingSpotLocked(spots);
-        if (!spot) {
-            spot = spots[0];
-            spots.erase(spots.begin());
-        }
-        releaseSpotLocked(spot);
-    }
-
-    // Obtain a sprite from the recycled pool.
-    sp<Sprite> sprite;
-    if (! mLocked.recycledSprites.empty()) {
-        sprite = mLocked.recycledSprites.back();
-        mLocked.recycledSprites.pop_back();
-    } else {
-        sprite = mSpriteController->createSprite();
-    }
-
-    // Return the new spot.
-    Spot* spot = new Spot(id, sprite);
-    spots.push_back(spot);
-    return spot;
-}
-
-PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) {
-    for (size_t i = 0; i < spots.size(); i++) {
-        Spot* spot = spots[i];
-        if (spot->id == Spot::INVALID_ID) {
-            spots.erase(spots.begin() + i);
-            return spot;
-        }
-    }
-    return nullptr;
-}
-
-void PointerController::releaseSpotLocked(Spot* spot) {
-    spot->sprite->clearIcon();
-
-    if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
-        mLocked.recycledSprites.push_back(spot->sprite);
-    }
-
-    delete spot;
-}
-
-void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
-    if (spot->id != Spot::INVALID_ID) {
-        spot->id = Spot::INVALID_ID;
-        startAnimationLocked();
-    }
-}
-
-void PointerController::fadeOutAndReleaseAllSpotsLocked() {
-    for (auto& it : mLocked.spotsByDisplay) {
-        const std::vector<Spot*>& spots = it.second;
-        size_t numSpots = spots.size();
-        for (size_t i = 0; i < numSpots; i++) {
-            Spot* spot = spots[i];
-            fadeOutAndReleaseSpotLocked(spot);
-        }
-    }
-}
-
-void PointerController::loadResourcesLocked() REQUIRES(mLock) {
-    if (!mLocked.viewport.isValid()) {
-        return;
-    }
-
-    mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
-    mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
-
-    mLocked.additionalMouseResources.clear();
-    mLocked.animationResources.clear();
-    if (mLocked.presentation == Presentation::POINTER) {
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                &mLocked.animationResources, mLocked.viewport.displayId);
-    }
-
-    mLocked.pointerIconChanged = true;
-}
-
-
-// --- PointerController::Spot ---
-
-void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
-        int32_t displayId) {
-    sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
-    sprite->setAlpha(alpha);
-    sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
-    sprite->setPosition(x, y);
-    sprite->setDisplayId(displayId);
-    this->x = x;
-    this->y = y;
-
-    if (icon != lastIcon) {
-        lastIcon = icon;
-        if (icon) {
-            sprite->setIcon(*icon);
-            sprite->setVisible(true);
-        } else {
-            sprite->setVisible(false);
+            ++it;
         }
     }
 }
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 14c0679..1f561da 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -30,48 +30,14 @@
 #include <memory>
 #include <vector>
 
+#include "MouseCursorController.h"
+#include "PointerControllerContext.h"
 #include "SpriteController.h"
+#include "TouchSpotController.h"
 
 namespace android {
 
 /*
- * Pointer resources.
- */
-struct PointerResources {
-    SpriteIcon spotHover;
-    SpriteIcon spotTouch;
-    SpriteIcon spotAnchor;
-};
-
-struct PointerAnimation {
-    std::vector<SpriteIcon> animationFrames;
-    nsecs_t durationPerFrame;
-};
-
-/*
- * Pointer controller policy interface.
- *
- * The pointer controller policy is used by the pointer controller to interact with
- * the Window Manager and other system components.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI.  This interface is also mocked in the unit tests.
- */
-class PointerControllerPolicyInterface : public virtual RefBase {
-protected:
-    PointerControllerPolicyInterface() { }
-    virtual ~PointerControllerPolicyInterface() { }
-
-public:
-    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0;
-    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0;
-    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
-            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
-    virtual int32_t getDefaultPointerIconId() = 0;
-    virtual int32_t getCustomPointerIconId() = 0;
-};
-
-/*
  * Tracks pointer movements and draws the pointer sprite to a surface.
  *
  * Handles pointer acceleration and animation.
@@ -81,15 +47,10 @@
     static std::shared_ptr<PointerController> create(
             const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
             const sp<SpriteController>& spriteController);
-    enum class InactivityTimeout {
-        NORMAL = 0,
-        SHORT = 1,
-    };
 
-    virtual ~PointerController();
+    virtual ~PointerController() = default;
 
-    virtual bool getBounds(float* outMinX, float* outMinY,
-            float* outMaxX, float* outMaxY) const;
+    virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     virtual void move(float deltaX, float deltaY);
     virtual void setButtonState(int32_t buttonState);
     virtual int32_t getButtonState() const;
@@ -101,129 +62,37 @@
     virtual void setDisplayViewport(const DisplayViewport& viewport);
 
     virtual void setPresentation(Presentation presentation);
-    virtual void setSpots(const PointerCoords* spotCoords,
-            const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId);
+    virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                          BitSet32 spotIdBits, int32_t displayId);
     virtual void clearSpots();
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
+    void doInactivityTimeout();
+    void doAnimate(nsecs_t timestamp);
     void reloadPointerResources();
+    void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
 
 private:
-    static constexpr size_t MAX_RECYCLED_SPRITES = 12;
-    static constexpr size_t MAX_SPOTS = 12;
+    friend PointerControllerContext::LooperCallback;
+    friend PointerControllerContext::MessageHandler;
 
-    enum {
-        MSG_INACTIVITY_TIMEOUT,
-    };
+    mutable std::mutex mLock;
 
-    struct Spot {
-        static const uint32_t INVALID_ID = 0xffffffff;
+    PointerControllerContext mContext;
 
-        uint32_t id;
-        sp<Sprite> sprite;
-        float alpha;
-        float scale;
-        float x, y;
-
-        inline Spot(uint32_t id, const sp<Sprite>& sprite)
-              : id(id),
-                sprite(sprite),
-                alpha(1.0f),
-                scale(1.0f),
-                x(0.0f),
-                y(0.0f),
-                lastIcon(nullptr) {}
-
-        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
-
-    private:
-        const SpriteIcon* lastIcon;
-    };
-
-    class MessageHandler : public virtual android::MessageHandler {
-    public:
-        void handleMessage(const Message& message) override;
-        std::weak_ptr<PointerController> pointerController;
-    };
-
-    class LooperCallback : public virtual android::LooperCallback {
-    public:
-        int handleEvent(int fd, int events, void* data) override;
-        std::weak_ptr<PointerController> pointerController;
-    };
-
-    mutable Mutex mLock;
-
-    sp<PointerControllerPolicyInterface> mPolicy;
-    sp<Looper> mLooper;
-    sp<SpriteController> mSpriteController;
-    sp<MessageHandler> mHandler;
-    sp<LooperCallback> mCallback;
-
-    DisplayEventReceiver mDisplayEventReceiver;
-
-    PointerResources mResources;
+    MouseCursorController mCursorController;
 
     struct Locked {
-        bool animationPending;
-        nsecs_t animationTime;
-
-        size_t animationFrameIndex;
-        nsecs_t lastFrameUpdatedTime;
-
-        DisplayViewport viewport;
-
-        InactivityTimeout inactivityTimeout;
-
         Presentation presentation;
-        bool presentationChanged;
 
-        int32_t pointerFadeDirection;
-        float pointerX;
-        float pointerY;
-        float pointerAlpha;
-        sp<Sprite> pointerSprite;
-        SpriteIcon pointerIcon;
-        bool pointerIconChanged;
-
-        std::map<int32_t, SpriteIcon> additionalMouseResources;
-        std::map<int32_t, PointerAnimation> animationResources;
-
-        int32_t requestedPointerType;
-
-        int32_t buttonState;
-
-        std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
-        std::vector<sp<Sprite>> recycledSprites;
+        std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
     } mLocked GUARDED_BY(mLock);
 
     PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
                       const sp<SpriteController>& spriteController);
-
-    bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
-    void setPositionLocked(float x, float y);
-
-    void doAnimate(nsecs_t timestamp);
-    bool doFadingAnimationLocked(nsecs_t timestamp);
-    bool doBitmapAnimationLocked(nsecs_t timestamp);
-    void doInactivityTimeout();
-
-    void startAnimationLocked();
-
-    void resetInactivityTimeoutLocked();
-    void removeInactivityTimeoutLocked();
-    void updatePointerLocked();
-
-    Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
-    Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
-    Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
-    void releaseSpotLocked(Spot* spot);
-    void fadeOutAndReleaseSpotLocked(Spot* spot);
-    void fadeOutAndReleaseAllSpotsLocked();
-
-    void loadResourcesLocked();
+    void clearSpotsLocked();
 };
 
 } // namespace android
diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp
new file mode 100644
index 0000000..2d7e22b
--- /dev/null
+++ b/libs/input/PointerControllerContext.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "PointerControllerContext.h"
+#include "PointerController.h"
+
+namespace {
+// Time to wait before starting the fade when the pointer is inactive.
+const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
+const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL;   // 3 seconds
+
+// The number of events to be read at once for DisplayEventReceiver.
+const int EVENT_BUFFER_SIZE = 100;
+} // namespace
+
+namespace android {
+
+// --- PointerControllerContext ---
+
+PointerControllerContext::PointerControllerContext(
+        const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController, PointerController& controller)
+      : mPolicy(policy),
+        mLooper(looper),
+        mSpriteController(spriteController),
+        mHandler(new MessageHandler()),
+        mCallback(new LooperCallback()),
+        mController(controller) {
+    std::scoped_lock lock(mLock);
+    mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
+    mLocked.animationPending = false;
+}
+
+PointerControllerContext::~PointerControllerContext() {
+    mLooper->removeMessages(mHandler);
+}
+
+void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
+    std::scoped_lock lock(mLock);
+
+    if (mLocked.inactivityTimeout != inactivityTimeout) {
+        mLocked.inactivityTimeout = inactivityTimeout;
+        resetInactivityTimeoutLocked();
+    }
+}
+
+void PointerControllerContext::startAnimation() {
+    std::scoped_lock lock(mLock);
+    if (!mLocked.animationPending) {
+        mLocked.animationPending = true;
+        mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        mDisplayEventReceiver.requestNextVsync();
+    }
+}
+
+void PointerControllerContext::resetInactivityTimeout() {
+    std::scoped_lock lock(mLock);
+    resetInactivityTimeoutLocked();
+}
+
+void PointerControllerContext::resetInactivityTimeoutLocked() REQUIRES(mLock) {
+    mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
+
+    nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+            : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+    mLooper->sendMessageDelayed(timeout, mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
+}
+
+void PointerControllerContext::removeInactivityTimeout() {
+    std::scoped_lock lock(mLock);
+    mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
+}
+
+void PointerControllerContext::setAnimationPending(bool animationPending) {
+    std::scoped_lock lock(mLock);
+    mLocked.animationPending = animationPending;
+}
+
+nsecs_t PointerControllerContext::getAnimationTime() {
+    std::scoped_lock lock(mLock);
+    return mLocked.animationTime;
+}
+
+void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) {
+    mHandler->pointerController = controller;
+}
+
+void PointerControllerContext::setCallbackController(
+        std::shared_ptr<PointerController> controller) {
+    mCallback->pointerController = controller;
+}
+
+sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() {
+    return mPolicy;
+}
+
+sp<SpriteController> PointerControllerContext::getSpriteController() {
+    return mSpriteController;
+}
+
+void PointerControllerContext::initializeDisplayEventReceiver() {
+    if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
+        mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, Looper::EVENT_INPUT,
+                       mCallback, nullptr);
+    } else {
+        ALOGE("Failed to initialize DisplayEventReceiver.");
+    }
+}
+
+void PointerControllerContext::handleDisplayEvents() {
+    bool gotVsync = false;
+    ssize_t n;
+    nsecs_t timestamp;
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
+            if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+                timestamp = buf[i].header.timestamp;
+                gotVsync = true;
+            }
+        }
+    }
+    if (gotVsync) {
+        mController.doAnimate(timestamp);
+    }
+}
+
+void PointerControllerContext::MessageHandler::handleMessage(const Message& message) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+
+    if (controller == nullptr) {
+        ALOGE("PointerController instance was released before processing message: what=%d",
+              message.what);
+        return;
+    }
+    switch (message.what) {
+        case MSG_INACTIVITY_TIMEOUT:
+            controller->doInactivityTimeout();
+            break;
+    }
+}
+
+int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int events,
+                                                          void* /* data */) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+    if (controller == nullptr) {
+        ALOGW("PointerController instance was released with pending callbacks.  events=0x%x",
+              events);
+        return 0; // Remove the callback, the PointerController is gone anyways
+    }
+    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+        ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events);
+        return 0; // remove the callback
+    }
+
+    if (!(events & Looper::EVENT_INPUT)) {
+        ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
+        return 1; // keep the callback
+    }
+
+    controller->mContext.handleDisplayEvents();
+    return 1; // keep the callback
+}
+
+} // namespace android
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
new file mode 100644
index 0000000..92e1bda
--- /dev/null
+++ b/libs/input/PointerControllerContext.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _UI_POINTER_CONTROLLER_CONTEXT_H
+#define _UI_POINTER_CONTROLLER_CONTEXT_H
+
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <ui/DisplayInfo.h>
+#include <utils/BitSet.h>
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
+
+namespace android {
+
+class PointerController;
+
+/*
+ * Pointer resources.
+ */
+struct PointerResources {
+    SpriteIcon spotHover;
+    SpriteIcon spotTouch;
+    SpriteIcon spotAnchor;
+};
+
+struct PointerAnimation {
+    std::vector<SpriteIcon> animationFrames;
+    nsecs_t durationPerFrame;
+};
+
+enum class InactivityTimeout {
+    NORMAL = 0,
+    SHORT = 1,
+};
+
+/*
+ * Pointer controller policy interface.
+ *
+ * The pointer controller policy is used by the pointer controller to interact with
+ * the Window Manager and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class PointerControllerPolicyInterface : public virtual RefBase {
+protected:
+    PointerControllerPolicyInterface() {}
+    virtual ~PointerControllerPolicyInterface() {}
+
+public:
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0;
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0;
+    virtual void loadAdditionalMouseResources(
+            std::map<int32_t, SpriteIcon>* outResources,
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0;
+    virtual int32_t getDefaultPointerIconId() = 0;
+    virtual int32_t getCustomPointerIconId() = 0;
+};
+
+/*
+ * Contains logic and resources shared among PointerController,
+ * MouseCursorController, and TouchSpotController.
+ */
+
+class PointerControllerContext {
+public:
+    PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy,
+                             const sp<Looper>& looper, const sp<SpriteController>& spriteController,
+                             PointerController& controller);
+    ~PointerControllerContext();
+
+    void removeInactivityTimeout();
+    void resetInactivityTimeout();
+    void startAnimation();
+    void setInactivityTimeout(InactivityTimeout inactivityTimeout);
+
+    void setAnimationPending(bool animationPending);
+    nsecs_t getAnimationTime();
+
+    void clearSpotsByDisplay(int32_t displayId);
+
+    void setHandlerController(std::shared_ptr<PointerController> controller);
+    void setCallbackController(std::shared_ptr<PointerController> controller);
+
+    sp<PointerControllerPolicyInterface> getPolicy();
+    sp<SpriteController> getSpriteController();
+
+    void initializeDisplayEventReceiver();
+    void handleDisplayEvents();
+
+    class MessageHandler : public virtual android::MessageHandler {
+    public:
+        enum {
+            MSG_INACTIVITY_TIMEOUT,
+        };
+
+        void handleMessage(const Message& message) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
+    class LooperCallback : public virtual android::LooperCallback {
+    public:
+        int handleEvent(int fd, int events, void* data) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
+private:
+    sp<PointerControllerPolicyInterface> mPolicy;
+    sp<Looper> mLooper;
+    sp<SpriteController> mSpriteController;
+    sp<MessageHandler> mHandler;
+    sp<LooperCallback> mCallback;
+
+    DisplayEventReceiver mDisplayEventReceiver;
+
+    PointerController& mController;
+
+    mutable std::mutex mLock;
+
+    struct Locked {
+        bool animationPending;
+        nsecs_t animationTime;
+
+        InactivityTimeout inactivityTimeout;
+    } mLocked GUARDED_BY(mLock);
+
+    void resetInactivityTimeoutLocked();
+};
+
+} // namespace android
+
+#endif // _UI_POINTER_CONTROLLER_CONTEXT_H
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
new file mode 100644
index 0000000..c7430ce
--- /dev/null
+++ b/libs/input/TouchSpotController.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "TouchSpotController"
+
+// Log debug messages about pointer updates
+#define DEBUG_SPOT_UPDATES 0
+
+#include "TouchSpotController.h"
+
+#include <log/log.h>
+
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+
+namespace {
+// Time to spend fading out the spot completely.
+const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
+} // namespace
+
+namespace android {
+
+// --- Spot ---
+
+void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+                                             int32_t displayId) {
+    sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
+    sprite->setAlpha(alpha);
+    sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
+    sprite->setPosition(x, y);
+    sprite->setDisplayId(displayId);
+    this->x = x;
+    this->y = y;
+
+    if (icon != mLastIcon) {
+        mLastIcon = icon;
+        if (icon) {
+            sprite->setIcon(*icon);
+            sprite->setVisible(true);
+        } else {
+            sprite->setVisible(false);
+        }
+    }
+}
+
+// --- TouchSpotController ---
+
+TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
+      : mDisplayId(displayId), mContext(context) {
+    mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
+}
+
+TouchSpotController::~TouchSpotController() {
+    std::scoped_lock lock(mLock);
+
+    size_t numSpots = mLocked.displaySpots.size();
+    for (size_t i = 0; i < numSpots; i++) {
+        delete mLocked.displaySpots[i];
+    }
+    mLocked.displaySpots.clear();
+}
+
+void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                                   BitSet32 spotIdBits) {
+#if DEBUG_SPOT_UPDATES
+    ALOGD("setSpots: idBits=%08x", spotIdBits.value);
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        uint32_t id = idBits.firstMarkedBit();
+        idBits.clearBit(id);
+        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
+              c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y),
+              c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId);
+    }
+#endif
+
+    std::scoped_lock lock(mLock);
+    sp<SpriteController> spriteController = mContext.getSpriteController();
+    spriteController->openTransaction();
+
+    // Add or move spots for fingers that are down.
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+        const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
+                ? mResources.spotTouch
+                : mResources.spotHover;
+        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
+        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+
+        Spot* spot = getSpot(id, mLocked.displaySpots);
+        if (!spot) {
+            spot = createAndAddSpotLocked(id, mLocked.displaySpots);
+        }
+
+        spot->updateSprite(&icon, x, y, mDisplayId);
+    }
+
+    for (Spot* spot : mLocked.displaySpots) {
+        if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) {
+            fadeOutAndReleaseSpotLocked(spot);
+        }
+    }
+
+    spriteController->closeTransaction();
+}
+
+void TouchSpotController::clearSpots() {
+#if DEBUG_SPOT_UPDATES
+    ALOGD("clearSpots");
+#endif
+
+    std::scoped_lock lock(mLock);
+    fadeOutAndReleaseAllSpotsLocked();
+}
+
+TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id,
+                                                        const std::vector<Spot*>& spots) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
+        if (spot->id == id) {
+            return spot;
+        }
+    }
+    return nullptr;
+}
+
+TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
+                                                                       std::vector<Spot*>& spots) {
+    // Remove spots until we have fewer than MAX_SPOTS remaining.
+    while (spots.size() >= MAX_SPOTS) {
+        Spot* spot = removeFirstFadingSpotLocked(spots);
+        if (!spot) {
+            spot = spots[0];
+            spots.erase(spots.begin());
+        }
+        releaseSpotLocked(spot);
+    }
+
+    // Obtain a sprite from the recycled pool.
+    sp<Sprite> sprite;
+    if (!mLocked.recycledSprites.empty()) {
+        sprite = mLocked.recycledSprites.back();
+        mLocked.recycledSprites.pop_back();
+    } else {
+        sprite = mContext.getSpriteController()->createSprite();
+    }
+
+    // Return the new spot.
+    Spot* spot = new Spot(id, sprite);
+    spots.push_back(spot);
+    return spot;
+}
+
+TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked(
+        std::vector<Spot*>& spots) REQUIRES(mLock) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
+        if (spot->id == Spot::INVALID_ID) {
+            spots.erase(spots.begin() + i);
+            return spot;
+        }
+    }
+    return NULL;
+}
+
+void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) {
+    spot->sprite->clearIcon();
+
+    if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
+        mLocked.recycledSprites.push_back(spot->sprite);
+    }
+
+    delete spot;
+}
+
+void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
+    if (spot->id != Spot::INVALID_ID) {
+        spot->id = Spot::INVALID_ID;
+        mContext.startAnimation();
+    }
+}
+
+void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) {
+    size_t numSpots = mLocked.displaySpots.size();
+    for (size_t i = 0; i < numSpots; i++) {
+        Spot* spot = mLocked.displaySpots[i];
+        fadeOutAndReleaseSpotLocked(spot);
+    }
+}
+
+void TouchSpotController::reloadSpotResources() {
+    mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
+}
+
+bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) {
+    std::scoped_lock lock(mLock);
+    nsecs_t animationTime = mContext.getAnimationTime();
+    nsecs_t frameDelay = timestamp - animationTime;
+    size_t numSpots = mLocked.displaySpots.size();
+    for (size_t i = 0; i < numSpots;) {
+        Spot* spot = mLocked.displaySpots[i];
+        if (spot->id == Spot::INVALID_ID) {
+            spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+            if (spot->alpha <= 0) {
+                mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i);
+                releaseSpotLocked(spot);
+                numSpots--;
+                continue;
+            } else {
+                spot->sprite->setAlpha(spot->alpha);
+                keepAnimating = true;
+            }
+        }
+        ++i;
+    }
+    return keepAnimating;
+}
+
+} // namespace android
diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h
new file mode 100644
index 0000000..f3b3550
--- /dev/null
+++ b/libs/input/TouchSpotController.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _UI_TOUCH_SPOT_CONTROLLER_H
+#define _UI_TOUCH_SPOT_CONTROLLER_H
+
+#include "PointerControllerContext.h"
+
+namespace android {
+
+/*
+ * Helper class for PointerController that specifically handles
+ * touch spot resources and actions for a single display.
+ */
+class TouchSpotController {
+public:
+    TouchSpotController(int32_t displayId, PointerControllerContext& context);
+    ~TouchSpotController();
+    void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                  BitSet32 spotIdBits);
+    void clearSpots();
+
+    void reloadSpotResources();
+    bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating);
+
+private:
+    struct Spot {
+        static const uint32_t INVALID_ID = 0xffffffff;
+
+        uint32_t id;
+        sp<Sprite> sprite;
+        float alpha;
+        float scale;
+        float x, y;
+
+        inline Spot(uint32_t id, const sp<Sprite>& sprite)
+              : id(id),
+                sprite(sprite),
+                alpha(1.0f),
+                scale(1.0f),
+                x(0.0f),
+                y(0.0f),
+                mLastIcon(nullptr) {}
+
+        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
+
+    private:
+        const SpriteIcon* mLastIcon;
+    };
+
+    int32_t mDisplayId;
+
+    mutable std::mutex mLock;
+
+    PointerResources mResources;
+
+    PointerControllerContext& mContext;
+
+    static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+    static constexpr size_t MAX_SPOTS = 12;
+
+    struct Locked {
+        std::vector<Spot*> displaySpots;
+        std::vector<sp<Sprite>> recycledSprites;
+
+    } mLocked GUARDED_BY(mLock);
+
+    Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
+    Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
+    Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
+    void releaseSpotLocked(Spot* spot);
+    void fadeOutAndReleaseSpotLocked(Spot* spot);
+    void fadeOutAndReleaseAllSpotsLocked();
+};
+
+} // namespace android
+
+#endif // _UI_TOUCH_SPOT_CONTROLLER_H
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 6e129a0..b67088a 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -178,9 +178,6 @@
     viewport.deviceWidth = 400;
     viewport.deviceHeight = 300;
     mPointerController->setDisplayViewport(viewport);
-
-    // The first call to setDisplayViewport should trigger the loading of the necessary resources.
-    EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
 }
 
 void PointerControllerTest::loopThread() {
@@ -208,6 +205,7 @@
 
 TEST_F(PointerControllerTest, updatePointerIcon) {
     ensureDisplayViewportIsSet();
+    mPointerController->setPresentation(PointerController::Presentation::POINTER);
     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     int32_t type = CURSOR_TYPE_ADDITIONAL;
@@ -247,8 +245,6 @@
 
 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
     mPointerController->setPresentation(PointerController::Presentation::POINTER);
-    mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
-    mPointerController->clearSpots();
     mPointerController->setPosition(1.0f, 1.0f);
     mPointerController->move(1.0f, 1.0f);
     mPointerController->unfade(PointerController::Transition::IMMEDIATE);
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 0282713..df45c9e 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -16,6 +16,7 @@
   }
 
   public interface Parcelable {
+    method public default int getStability();
     field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0
     field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1
   }
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 68da999..e937a51 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -48,7 +48,7 @@
         "androidx.lifecycle_lifecycle-extensions",
         "SystemUI-tags",
         "SystemUI-proto",
-        "dagger2-2.19",
+        "dagger2",
         "//external/kotlinc:kotlin-annotations",
     ],
 
@@ -59,7 +59,7 @@
 
     manifest: "AndroidManifest.xml",
 
-    plugins: ["dagger2-compiler-2.19"],
+    plugins: ["dagger2-compiler"],
 
 }
 
@@ -100,7 +100,7 @@
 
     kotlincflags: ["-Xjvm-default=enable"],
 
-    plugins: ["dagger2-compiler-2.19"],
+    plugins: ["dagger2-compiler"],
 
     required: ["privapp_whitelist_com.android.systemui"],
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SignalStrengthUtil.java b/packages/SettingsLib/src/com/android/settingslib/net/SignalStrengthUtil.java
index 246f2ce..e1174fa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/SignalStrengthUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SignalStrengthUtil.java
@@ -17,7 +17,8 @@
 package com.android.settingslib.net;
 
 import android.content.Context;
-import android.telephony.SubscriptionManager;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 
 /**
  * Utilities for dealing with signal strength.
@@ -28,7 +29,13 @@
      * bar for the subscription with the given id
      */
     public static boolean shouldInflateSignalStrength(Context context, int subscriptionId) {
-        return SubscriptionManager.getResourcesForSubId(context, subscriptionId)
-                .getBoolean(com.android.internal.R.bool.config_inflateSignalStrength);
+        final CarrierConfigManager carrierConfigMgr =
+                context.getSystemService(CarrierConfigManager.class);
+        PersistableBundle bundle = null;
+        if (carrierConfigMgr != null) {
+            bundle = carrierConfigMgr.getConfigForSubId(subscriptionId);
+        }
+        return (bundle != null && bundle.getBoolean(
+                CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false));
     }
 }
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index dbfaca0..a645dfc 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -62,7 +62,7 @@
         "iconloader_base",
         "SystemUI-tags",
         "SystemUI-proto",
-        "dagger2-2.19",
+        "dagger2",
         "jsr330"
     ],
     manifest: "AndroidManifest.xml",
@@ -73,7 +73,7 @@
 
     kotlincflags: ["-Xjvm-default=enable"],
 
-    plugins: ["dagger2-compiler-2.19"],
+    plugins: ["dagger2-compiler"],
 }
 
 android_library {
@@ -118,7 +118,7 @@
         "mockito-target-inline-minus-junit4",
         "testables",
         "truth-prebuilt",
-        "dagger2-2.19",
+        "dagger2",
         "jsr330"
     ],
     libs: [
@@ -131,7 +131,7 @@
         "--extra-packages",
         "com.android.systemui",
     ],
-    plugins: ["dagger2-compiler-2.19"],
+    plugins: ["dagger2-compiler"],
 }
 
 android_app {
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index c440fba..bb68647 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -206,11 +206,31 @@
 
 ## Updating Dagger2
 
+We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
+when that repository is updated.
+
+*Deprecated:*
+
 Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
 into
 [/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
 
+The following commands should work, substituting in the version that you are looking for:
 
+````
+cd prebuilts/tools/common/m2/repository/com/google/dagger/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
+````
+
+Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
+ 
 ## TODO List
 
  - Eliminate usages of Dependency#get
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 3b08ef5..e4feaae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -1157,7 +1157,6 @@
         boolean show4gForLte = false;
         boolean hideLtePlus = false;
         boolean hspaDataDistinguishable;
-        boolean inflateSignalStrengths = false;
         boolean alwaysShowDataRatIcon = false;
 
         static Config readConfig(Context context) {
@@ -1169,8 +1168,6 @@
                     res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
             config.hspaDataDistinguishable =
                     res.getBoolean(R.bool.config_hspa_data_distinguishable);
-            config.inflateSignalStrengths = res.getBoolean(
-                    com.android.internal.R.bool.config_inflateSignalStrength);
 
             CarrierConfigManager configMgr = (CarrierConfigManager)
                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index 2c4059d..591861f 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -1,17 +1,8 @@
 # These must be kept in sync with the framework-tethering-shared-srcs filegroup.
-# If there are files in that filegroup that do not appear here, the classes in the
+# Classes from the framework-tethering-shared-srcs filegroup.
+# If there are files in that filegroup that are not covered below, the classes in the
 # module will be overwritten by the ones in the framework.
-# Don't jar-jar the entire package because tethering still use some internal classes
-# (like TrafficStatsConstants in com.android.internal.util)
-# TODO: simply these when tethering is built as system_current.
-rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1
-rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
-rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
-rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
-rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
-rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
-rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
-
+rule com.android.internal.util.** com.android.networkstack.tethering.util.@1
 rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
 
 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index 4710a30..aaaec17 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -16,7 +16,7 @@
 
 package android.net.dhcp;
 
-import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
+import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
 
 import android.net.LinkAddress;
 import android.util.ArraySet;
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 4c155ac..673cbf0 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -19,13 +19,14 @@
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
 import static android.net.util.PrefixUtils.asIpPrefix;
 import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
+import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
 import android.net.IpPrefix;
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index e0a1dc7..3b72b5b 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -36,7 +36,8 @@
 import static android.net.netlink.StructNdMsg.NUD_FAILED;
 import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
 import static android.net.netlink.StructNdMsg.NUD_STALE;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
+
+import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 4b6bbac..75c819b 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -18,33 +18,40 @@
 
 import android.app.Notification
 import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
 import android.content.Context
+import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.res.Resources
 import android.net.ConnectivityManager.TETHERING_WIFI
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.Looper
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
 import android.os.UserHandle
+import android.provider.Settings
 import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
 import com.android.internal.util.test.BroadcastInterceptingContext
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ACTION_DISABLE_TETHERING
 import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
 import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
 import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
 import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
 import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
 import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.getSettingsPackageName
 import com.android.testutils.waitForIdle
 import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Test
@@ -87,12 +94,17 @@
     // every test but should always be initialized before use (or the test should crash).
     private lateinit var context: TestContext
     private lateinit var notificationUpdater: TetheringNotificationUpdater
+
+    // Initializing the following members depends on initializing some of the mocks and
+    // is more logically done in setup().
     private lateinit var fakeTetheringThread: HandlerThread
 
     private val ROAMING_CAPABILITIES = NetworkCapabilities()
     private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
     private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
     private val TIMEOUT_MS = 500L
+    private val ACTIVITY_PENDING_INTENT = 0
+    private val BROADCAST_PENDING_INTENT = 1
 
     private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
         override fun createContextAsUser(user: UserHandle, flags: Int) =
@@ -146,10 +158,43 @@
         fakeTetheringThread.quitSafely()
     }
 
+    private fun verifyActivityPendingIntent(intent: Intent, flags: Int) {
+        // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
+        // the flag in creating arguments). If the described PendingIntent does not already exist,
+        // getActivity() will return null instead of PendingIntent object.
+        val pi = PendingIntent.getActivity(
+                context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                intent,
+                flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE,
+                null /* options */)
+        assertNotNull("Activity PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
+    }
+
+    private fun verifyBroadcastPendingIntent(intent: Intent, flags: Int) {
+        // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
+        // the flag in creating arguments). If the described PendingIntent does not already exist,
+        // getBroadcast() will return null instead of PendingIntent object.
+        val pi = PendingIntent.getBroadcast(
+                context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                intent,
+                flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE)
+        assertNotNull("Broadcast PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
+    }
+
     private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
     private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
 
-    private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
+    private fun verifyNotification(
+        iconId: Int,
+        title: String,
+        text: String,
+        id: Int,
+        intentSenderType: Int,
+        intent: Intent,
+        flags: Int
+    ) {
         verify(notificationManager, never()).cancel(any(), eq(id))
 
         val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
@@ -161,6 +206,11 @@
         assertEquals(title, notification.title())
         assertEquals(text, notification.text())
 
+        when (intentSenderType) {
+            ACTIVITY_PENDING_INTENT -> verifyActivityPendingIntent(intent, flags)
+            BROADCAST_PENDING_INTENT -> verifyBroadcastPendingIntent(intent, flags)
+        }
+
         reset(notificationManager)
     }
 
@@ -176,6 +226,10 @@
 
     @Test
     fun testRestrictedNotification() {
+        val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
+                .setPackage(getSettingsPackageName(context.packageManager))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
         // Set test sub id.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
         verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -183,7 +237,7 @@
         // User restrictions on. Show restricted notification.
         notificationUpdater.notifyTetheringDisabledByRestriction()
         verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
-                RESTRICTED_NOTIFICATION_ID)
+                RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
 
         // User restrictions off. Clear notification.
         notificationUpdater.tetheringRestrictionLifted()
@@ -196,7 +250,7 @@
         // User restrictions on again. Show restricted notification.
         notificationUpdater.notifyTetheringDisabledByRestriction()
         verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
-                RESTRICTED_NOTIFICATION_ID)
+                RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
     }
 
     val MAX_BACKOFF_MS = 200L
@@ -234,6 +288,8 @@
 
     @Test
     fun testNoUpstreamNotification() {
+        val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
+
         // Set test sub id.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
         verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -246,7 +302,8 @@
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // Same capabilities changed. Nothing happened.
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
@@ -260,7 +317,8 @@
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // No downstream.
         notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
@@ -305,6 +363,11 @@
 
     @Test
     fun testRoamingNotification() {
+        val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
+        val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
+                .setPackage(getSettingsPackageName(context.packageManager))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
         // Set test sub id.
         notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
         verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -316,7 +379,7 @@
         // Upstream capabilities changed to roaming state. Show roaming notification.
         notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
-                ROAMING_NOTIFICATION_ID)
+                ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
 
         // Same capabilities change. Nothing happened.
         notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
@@ -329,14 +392,15 @@
         // Upstream capabilities changed to roaming state again. Show roaming notification.
         notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
-                ROAMING_NOTIFICATION_ID)
+                ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
 
         // No upstream. Clear roaming notification and show no upstream notification.
         notificationUpdater.onUpstreamCapabilitiesChanged(null)
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // No downstream.
         notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
@@ -347,7 +411,8 @@
         notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
         verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
         verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
-                NO_UPSTREAM_NOTIFICATION_ID)
+                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
+                FLAG_IMMUTABLE)
 
         // Set R.bool.config_upstream_roaming_notification to false and change upstream
         // network to roaming state again. No roaming notification.
@@ -363,8 +428,7 @@
         val testSettingsPackageName = "com.android.test.settings"
         val pm = mock(PackageManager::class.java)
         doReturn(null).`when`(pm).resolveActivity(any(), anyInt())
-        assertEquals(defaultSettingsPackageName,
-                TetheringNotificationUpdater.getSettingsPackageName(pm))
+        assertEquals(defaultSettingsPackageName, getSettingsPackageName(pm))
 
         val resolveInfo = ResolveInfo().apply {
             activityInfo = ActivityInfo().apply {
@@ -375,7 +439,6 @@
             }
         }
         doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt())
-        assertEquals(testSettingsPackageName,
-                TetheringNotificationUpdater.getSettingsPackageName(pm))
+        assertEquals(testSettingsPackageName, getSettingsPackageName(pm))
     }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index e255737..fb3940b 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -40,7 +40,6 @@
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -49,6 +48,7 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
 import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
 import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e1bcb0c..81bbf68 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -220,6 +220,8 @@
 
 import com.google.android.collect.Lists;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -7504,18 +7506,34 @@
     public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId,
             int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
             String dstAddr) {
-        mKeepaliveTracker.startNattKeepalive(
-                getNetworkAgentInfoForNetwork(network), fd, resourceId,
-                intervalSeconds, cb,
-                srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
+        try {
+            mKeepaliveTracker.startNattKeepalive(
+                    getNetworkAgentInfoForNetwork(network), fd, resourceId,
+                    intervalSeconds, cb,
+                    srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
+        } finally {
+            // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
+            // startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately.
+            if (fd != null && Binder.getCallingPid() != Process.myPid()) {
+                IoUtils.closeQuietly(fd);
+            }
+        }
     }
 
     @Override
     public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds,
             ISocketKeepaliveCallback cb) {
-        enforceKeepalivePermission();
-        mKeepaliveTracker.startTcpKeepalive(
-                getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb);
+        try {
+            enforceKeepalivePermission();
+            mKeepaliveTracker.startTcpKeepalive(
+                    getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb);
+        } finally {
+            // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
+            // startTcpKeepalive calls Os.dup(fd) before returning, so we can close immediately.
+            if (fd != null && Binder.getCallingPid() != Process.myPid()) {
+                IoUtils.closeQuietly(fd);
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 994fca8..2657751 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2649,7 +2649,7 @@
 
     private void writePackageListLPrInternal(int creatingUserId) {
         // Only derive GIDs for active users (not dying)
-        final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true);
+        final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true);
         int[] userIds = new int[users.size()];
         for (int i = 0; i < userIds.length; i++) {
             userIds[i] = users.get(i).id;
@@ -4325,25 +4325,43 @@
     }
 
     /**
-     * Return all users on the device, including partial or dying users.
+     * Returns all users on the device, including pre-created and dying users.
+     *
      * @param userManager UserManagerService instance
      * @return the list of users
      */
     private static List<UserInfo> getAllUsers(UserManagerService userManager) {
-        return getUsers(userManager, false);
+        return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false);
     }
 
     /**
-     * Return the list of users on the device. Clear the calling identity before calling into
-     * UserManagerService.
+     * Returns the list of users on the device, excluding pre-created ones.
+     *
      * @param userManager UserManagerService instance
      * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     *
      * @return the list of users
      */
-    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) {
+    private static List<UserInfo> getActiveUsers(UserManagerService userManager,
+            boolean excludeDying) {
+        return getUsers(userManager, excludeDying, /* excludePreCreated= */ true);
+    }
+
+    /**
+     * Returns the list of users on the device.
+     *
+     * @param userManager UserManagerService instance
+     * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     * @param excludePreCreated Indicates whether to exclude any pre-created users.
+     *
+     * @return the list of users
+     */
+    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying,
+            boolean excludePreCreated) {
         long id = Binder.clearCallingIdentity();
         try {
-            return userManager.getUsers(excludeDying);
+            return userManager.getUsers(/* excludePartial= */ true, excludeDying,
+                    excludePreCreated);
         } catch (NullPointerException npe) {
             // packagemanager not yet initialized
         } finally {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 1f445c9..375a813 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -424,6 +424,10 @@
         AutoMutex _l(mLock);
         mLocked.viewports = viewports;
         mLocked.pointerDisplayId = pointerDisplayId;
+        std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
+        if (controller != nullptr) {
+            controller->onDisplayViewportsUpdated(mLocked.viewports);
+        }
     } // release lock
 
     mInputManager->getReader()->requestRefreshConfiguration(
@@ -847,8 +851,8 @@
     }
 
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
-    controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
-                                               : PointerController::InactivityTimeout::NORMAL);
+    controller->setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT
+                                               : InactivityTimeout::NORMAL);
 }
 
 void NativeInputManager::setPointerSpeed(int32_t speed) {
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 8b444b0..d071e0a5 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -19,46 +19,6 @@
     ],
 }
 
-// Version of services.net for usage by the wifi mainline module.
-// Note: This is compiled against module_current.
-// TODO(b/145825329): This should be moved to networkstack-client,
-// with dependencies moved to frameworks/libs/net right.
-java_library {
-    name: "services.net-module-wifi",
-    srcs: [
-        ":framework-services-net-module-wifi-shared-srcs",
-        ":net-module-utils-srcs",
-        "java/android/net/ip/IpClientCallbacks.java",
-        "java/android/net/ip/IpClientManager.java",
-        "java/android/net/ip/IpClientUtil.java",
-        "java/android/net/util/KeepalivePacketDataUtil.java",
-        "java/android/net/util/NetworkConstants.java",
-        "java/android/net/IpMemoryStore.java",
-        "java/android/net/NetworkMonitorManager.java",
-        "java/android/net/TcpKeepalivePacketData.java",
-    ],
-    sdk_version: "module_current",
-    libs: [
-        "unsupportedappusage",
-        "framework-wifi-util-lib",
-    ],
-    static_libs: [
-        // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the
-        // classes generated by netd_aidl_interfaces-platform-java above.
-        "netd_aidl_interface-V3-java",
-        "netlink-client",
-        "networkstack-client",
-        "net-utils-services-common",
-    ],
-    apex_available: [
-        "com.android.wifi",
-    ],
-    visibility: [
-        "//frameworks/opt/net/wifi/service",
-        "//frameworks/opt/net/wifi/tests/wifitests",
-    ],
-}
-
 filegroup {
     name: "services-tethering-shared-srcs",
     srcs: [
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index b172c4b..b17fcaa 100644
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
@@ -68,12 +68,13 @@
      */
     public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
         // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and
-        // would use a wrapper instead. But there are already two classes in the tree for DHCP
-        // information: DhcpInfo and DhcpResults, and each of them do not expose an appropriate API
-        // (they are bags of mutable fields and can't be changed because they are public API and
-        // @UnsupportedAppUsage). Adding a third class would cost more than the gain considering
-        // that the only client of this callback is WiFi, which will end up converting the results
-        // to DhcpInfo anyway.
+        // would use a wrapper instead, because of the lack of safety of stable parcelables. But
+        // there are already two classes in the tree for DHCP information: DhcpInfo and DhcpResults,
+        // and neither of them exposes an appropriate API (they are bags of mutable fields and can't
+        // be changed because they are public API and @UnsupportedAppUsage, being no better than the
+        // stable parcelable). Adding a third class would cost more than the gain considering that
+        // the only client of this callback is WiFi, which will end up converting the results to
+        // DhcpInfo anyway.
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3315e8d..0f73440 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1086,6 +1086,15 @@
         "show_signal_strength_in_sim_status_bool";
 
     /**
+     * Flag specifying if we should interpret all signal strength as one bar higher
+     * This is a replacement for the former resource config_inflateSignalStrength
+     * The default value is false.
+     * @hide
+     */
+    public static final String KEY_INFLATE_SIGNAL_STRENGTH_BOOL =
+            "inflate_signal_strength_bool";
+
+    /**
      * Flag specifying whether an additional (client initiated) intent needs to be sent on System
      * update
      */
@@ -3914,6 +3923,7 @@
         sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, true);
+        sDefaults.putBoolean(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
         sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/tests/net/common/java/android/net/DhcpInfoTest.java
index bd5533f..4d45ad7 100644
--- a/tests/net/common/java/android/net/DhcpInfoTest.java
+++ b/tests/net/common/java/android/net/DhcpInfoTest.java
@@ -16,8 +16,7 @@
 
 package android.net;
 
-import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTL;
-
+import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL;
 import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
 import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip;