Merge "sf: Update PowerAdvisor to talk to AIDL Power HAL"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e1a7bb8..02e5d9b 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1103,7 +1103,7 @@
 
 binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
         const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
-        const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+        int32_t appId, const std::string& seInfo,
         int32_t targetSdkVersion, const std::string& fromCodePath) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(fromUuid);
@@ -1114,24 +1114,24 @@
     const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
     const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
     const char* package_name = packageName.c_str();
-    const char* data_app_name = dataAppName.c_str();
 
     binder::Status res = ok();
     std::vector<userid_t> users = get_known_users(from_uuid);
 
+    auto to_app_package_path_parent = create_data_app_path(to_uuid);
+    auto to_app_package_path = StringPrintf("%s/%s", to_app_package_path_parent.c_str(),
+                                            android::base::Basename(fromCodePath).c_str());
+
     // Copy app
     {
-        auto to = create_data_app_package_path(to_uuid, data_app_name);
-        auto to_parent = create_data_app_path(to_uuid);
-
-        int rc = copy_directory_recursive(fromCodePath.c_str(), to_parent.c_str());
+        int rc = copy_directory_recursive(fromCodePath.c_str(), to_app_package_path_parent.c_str());
         if (rc != 0) {
-            res = error(rc, "Failed copying " + fromCodePath + " to " + to);
+            res = error(rc, "Failed copying " + fromCodePath + " to " + to_app_package_path);
             goto fail;
         }
 
-        if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-            res = error("Failed to restorecon " + to);
+        if (selinux_android_restorecon(to_app_package_path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            res = error("Failed to restorecon " + to_app_package_path);
             goto fail;
         }
     }
@@ -1188,9 +1188,8 @@
 fail:
     // Nuke everything we might have already copied
     {
-        auto to = create_data_app_package_path(to_uuid, data_app_name);
-        if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
-            LOG(WARNING) << "Failed to rollback " << to;
+        if (delete_dir_contents(to_app_package_path.c_str(), 1, nullptr) != 0) {
+            LOG(WARNING) << "Failed to rollback " << to_app_package_path;
         }
     }
     for (auto user : users) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 27c59b0..df01c3c 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -96,7 +96,7 @@
 
     binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
             const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
-            const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+            int32_t appId, const std::string& seInfo,
             int32_t targetSdkVersion, const std::string& fromCodePath);
 
     binder::Status dexopt(const std::string& apkPath, int32_t uid,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index f2e86ba..ca95cb3 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -51,7 +51,7 @@
     void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
 
     void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
-            @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
+            @utf8InCpp String packageName, int appId,
             @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
 
     void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index d236f76..ed87b67 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -331,13 +331,6 @@
             create_data_media_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10));
 }
 
-TEST_F(UtilsTest, CreateDataAppPackagePath) {
-    EXPECT_EQ("/data/app/com.example", create_data_app_package_path(nullptr, "com.example"));
-
-    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/app/com.example",
-            create_data_app_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", "com.example"));
-}
-
 TEST_F(UtilsTest, CreateDataUserPackagePath) {
     EXPECT_EQ("/data/data/com.example", create_data_user_ce_package_path(nullptr, 0, "com.example"));
     EXPECT_EQ("/data/user/10/com.example", create_data_user_ce_package_path(nullptr, 10, "com.example"));
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 939cf90..042d69e 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -100,18 +100,6 @@
 }
 
 /**
- * Create the path name where package app contents should be stored for
- * the given volume UUID and package name.  An empty UUID is assumed to
- * be internal storage.
- */
-std::string create_data_app_package_path(const char* volume_uuid,
-        const char* package_name) {
-    check_package_name(package_name);
-    return StringPrintf("%s/%s",
-            create_data_app_path(volume_uuid).c_str(), package_name);
-}
-
-/**
  * Create the path name where package data should be stored for the given
  * volume UUID, package name, and user ID. An empty UUID is assumed to be
  * internal storage.
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 6a39adc..01126c0 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -47,7 +47,6 @@
 std::string create_data_path(const char* volume_uuid);
 
 std::string create_data_app_path(const char* volume_uuid);
-std::string create_data_app_package_path(const char* volume_uuid, const char* package_name);
 
 std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid);
 std::string create_data_user_de_path(const char* volume_uuid, userid_t userid);
diff --git a/data/etc/android.hardware.tv.tuner.xml b/data/etc/android.hardware.tv.tuner.xml
new file mode 100644
index 0000000..bbf084f
--- /dev/null
+++ b/data/etc/android.hardware.tv.tuner.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This feature indicates that the device has tuner hardware and tuner HAL
+     implementation to support tuner operations. -->
+<permissions>
+    <feature name="android.hardware.tv.tuner" />
+</permissions>
diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h
index 477faed..b66efce 100644
--- a/headers/media_plugin/media/openmax/OMX_AudioExt.h
+++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h
@@ -116,6 +116,8 @@
     OMX_S32 nEncodedTargetLevel;   /**< Target reference level assumed at the encoder, between 0 and 127, -1 if unspecified */
     OMX_S32 nPCMLimiterEnable;     /**< Signal level limiting, 0 for disable, 1 for enable, -1 if unspecified */
     OMX_S32 nDrcEffectType;        /**< MPEG-D DRC effect type, between -1 and 6, -2 if unspecified */
+    OMX_S32 nDrcOutputLoudness;    /**< MPEG-D DRC Output Loudness, between -1 and 231, -2 if unspecified */
+    OMX_S32 nDrcAlbumMode;         /**< MPEG-D DRC Album Mode, between 0 and 1, -1 if unspecified */
 } OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE;
 
 typedef struct OMX_AUDIO_PARAM_ANDROID_PROFILETYPE {
diff --git a/include/android/CoolingDevice.h b/include/android/CoolingDevice.h
new file mode 100644
index 0000000..2f366be
--- /dev/null
+++ b/include/android/CoolingDevice.h
@@ -0,0 +1,49 @@
+/*
+ * 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 ANDROID_OS_COOLINGDEVICE_H
+#define ANDROID_OS_COOLINGDEVICE_H
+
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+/**
+ * CoolingDevice is a structure to encapsulate cooling device status.
+ */
+struct CoolingDevice : public android::Parcelable {
+    /** Current throttle state of the cooling device.  */
+    float mValue;
+    /** A cooling device type from ThermalHAL */
+    uint32_t mType;
+    /** Name of this cooling device */
+    String16 mName;
+
+    CoolingDevice()
+        : mValue(0.0f),
+          mType(0),
+          mName("") {
+    }
+    virtual status_t readFromParcel(const android::Parcel* parcel) override;
+    virtual status_t writeToParcel(android::Parcel* parcel) const override;
+};
+
+} // namespace os
+} // namespace android
+
+#endif /* ANDROID_OS_COOLINGDEVICE_H */
diff --git a/include/android/Temperature.h b/include/android/Temperature.h
new file mode 100644
index 0000000..2e68ec4
--- /dev/null
+++ b/include/android/Temperature.h
@@ -0,0 +1,53 @@
+/*
+ * 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 ANDROID_OS_TEMPERATURE_H
+#define ANDROID_OS_TEMPERATURE_H
+
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+/**
+ * Temperature is a structure to encapsulate temperature status.
+ */
+struct Temperature : public android::Parcelable {
+    /** Temperature value */
+    float mValue;
+    /** A Temperature type from ThermalHAL */
+    int32_t mType;
+    /** Name of this Temperature */
+    String16 mName;
+    /** The level of the sensor is currently in throttling */
+    int32_t mStatus;
+
+    Temperature()
+        : mValue(0.0f),
+          mType(0),
+          mName(""),
+          mStatus(0) {
+    }
+
+    virtual status_t readFromParcel(const android::Parcel* parcel) override;
+    virtual status_t writeToParcel(android::Parcel* parcel) const override;
+};
+
+} // namespace os
+} // namespace android
+
+#endif /* ANDROID_OS_TEMPERATURE_H */
diff --git a/include/android/thermal.h b/include/android/thermal.h
new file mode 100644
index 0000000..0f4b4d9
--- /dev/null
+++ b/include/android/thermal.h
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup Thermal
+ * @{
+ */
+
+/**
+ * @file thermal.h
+ */
+
+#ifndef _ANDROID_THERMAL_H
+#define _ANDROID_THERMAL_H
+
+#include <sys/cdefs.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit).
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/*
+ * Structures and functions to access thermal status and register/unregister
+ * thermal status listener in native code.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(30) /* Introduced in API level 30 */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __ANDROID_API__ >= 30
+
+enum AThermalStatus {
+    /** Error in thermal status. */
+    ATHERMAL_STATUS_ERROR = -1,
+    /** Not under throttling. */
+    ATHERMAL_STATUS_NONE = 0,
+    /** Light throttling where UX is not impacted. */
+    ATHERMAL_STATUS_LIGHT = 1,
+    /** Moderate throttling where UX is not largely impacted. */
+    ATHERMAL_STATUS_MODERATE = 2,
+    /** Severe throttling where UX is largely impacted. */
+    ATHERMAL_STATUS_SEVERE = 3,
+    /** Platform has done everything to reduce power. */
+    ATHERMAL_STATUS_CRITICAL = 4,
+    /**
+     * Key components in platform are shutting down due to thermal condition.
+     * Device functionalities will be limited.
+     */
+    ATHERMAL_STATUS_EMERGENCY = 5,
+    /** Need shutdown immediately. */
+    ATHERMAL_STATUS_SHUTDOWN = 6,
+};
+
+/**
+ * An opaque type representing a handle to a thermal manager.
+ * An instance of thermal manager must be acquired prior to
+ * using thermal status APIs and must be released after use.
+ *
+ * <p>To use:<ul>
+ *    <li>Create a new thermal manager instance by calling the
+ *        {@link AThermal_acquireManager} function.</li>
+ *    <li>Get current thermal status with
+ *        {@link AThermal_getCurrentThermalStatus}.</li>
+ *    <li>Register a thermal status listener with
+ *        {@link AThermal_registerThermalStatusListener}.</li>
+ *    <li>Unregister a thermal status listener with
+ *        {@link AThermal_unregisterThermalStatusListener}.</li>
+ *    <li>Release the thermal manager instance with
+ *        {@link AThermal_releaseManager}.</li></ul></p>
+ *
+ */
+typedef struct AThermalManager AThermalManager;
+
+/**
+ * Prototype of the function that is called when thermal status changes.
+ * It's passed the updated thermal status as parameter, as well as the
+ * pointer provided by the client that registered a callback.
+ */
+typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+
+/**
+  * Acquire an instance of the thermal manager. This must be freed using
+  * {@link AThermal_releaseManager}.
+  *
+  * @return manager instance on success, nullptr on failure.
+ */
+AThermalManager* AThermal_acquireManager();
+
+/**
+ * Release the thermal manager pointer acquired via
+ * {@link AThermal_acquireManager}.
+ *
+ * @param manager The manager to be released.
+ *
+ */
+void AThermal_releaseManager(AThermalManager *manager);
+
+/**
+  * Gets the current thermal status.
+  *
+  * @param manager The manager instance to use to query the thermal status.
+  * Acquired via {@link AThermal_acquireManager}.
+  *
+  * @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
+*/
+AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager);
+
+/**
+ * Register the thermal status listener for thermal status change.
+ *
+ * @param manager The manager instance to use to register.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called when thermal status updated.
+ * @param data The data pointer to be passed when callback is called.
+ *
+ * @return 0 on success
+ *         EINVAL if the listener and data pointer were previously added and not removed.
+ *         EPERM if the required permission is not held.
+ *         EPIPE if communication with the system service has failed.
+ */
+int AThermal_registerThermalStatusListener(AThermalManager *manager,
+        AThermal_StatusCallback callback, void *data);
+
+/**
+ * Unregister the thermal status listener previously resgistered.
+ *
+ * @param manager The manager instance to use to unregister.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called when thermal status updated.
+ * @param data The data pointer to be passed when callback is called.
+ *
+ * @return 0 on success
+ *         EINVAL if the listener and data pointer were not previously added.
+ *         EPERM if the required permission is not held.
+ *         EPIPE if communication with the system service has failed.
+ */
+int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
+        AThermal_StatusCallback callback, void *data);
+
+
+#endif  //  __ANDROID_API__ >= 30
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _ANDROID_THERMAL_H
+
+/** @} */
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index b6efc82..20a17e3 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -180,6 +180,8 @@
     VIRTUAL_KEYBOARD_ID = -1,
     // Device id of the "built-in" keyboard if there is one.
     BUILT_IN_KEYBOARD_ID = 0,
+    // First device id available for dynamic devices
+    END_RESERVED_ID = 1,
 };
 
 } // namespace android
diff --git a/include/powermanager/PowerManager.h b/include/powermanager/PowerManager.h
index 3268b45..9bac242 100644
--- a/include/powermanager/PowerManager.h
+++ b/include/powermanager/PowerManager.h
@@ -33,6 +33,17 @@
     USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_ACCESSIBILITY, // Last valid event code.
 };
 
+/** Keep in sync with android.os.temprature and hardware/interfaces/thermal/2.0/types.hal */
+enum class ThermalStatus : uint32_t {
+    THERMAL_STATUS_NONE = 0,
+    THERMAL_STATUS_LIGHT = 1,
+    THERMAL_STATUS_MODERATE = 2,
+    THERMAL_STATUS_SEVERE = 3,
+    THERMAL_STATUS_CRITICAL = 4,
+    THERMAL_STATUS_EMERGENCY = 5,
+    THERMAL_STATUS_SHUTDOWN = 6,
+};
+
 }; // namespace android
 
 #endif // ANDROID_POWERMANAGER_H
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index f064bd7..71d8130 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -64,8 +64,7 @@
 
 bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
                                             bool allowIsolated, int dumpFlags) {
-    auto manager = interface_cast<AidlServiceManager>(
-                    ProcessState::self()->getContextObject(nullptr));
+    auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     bool reRegister = mRegisteredServices.count(name) > 0;
     std::string regStr = (reRegister) ? "Re-registering" : "Registering";
@@ -114,9 +113,7 @@
 void ClientCounterCallback::tryShutdown() {
     ALOGI("Trying to shut down the service. No clients in use for any service in process.");
 
-    // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places.
-    auto manager = interface_cast<AidlServiceManager>(
-            ProcessState::self()->getContextObject(nullptr));
+    auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     auto unRegisterIt = mRegisteredServices.begin();
     for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9f8d752..beab270 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -560,6 +560,13 @@
 bool Parcel::enforceInterface(const String16& interface,
                               IPCThreadState* threadState) const
 {
+    return enforceInterface(interface.string(), interface.size(), threadState);
+}
+
+bool Parcel::enforceInterface(const char16_t* interface,
+                              size_t len,
+                              IPCThreadState* threadState) const
+{
     // StrictModePolicy.
     int32_t strictPolicy = readInt32();
     if (threadState == nullptr) {
@@ -586,12 +593,15 @@
         return false;
     }
     // Interface descriptor.
-    const String16 str(readString16());
-    if (str == interface) {
+    size_t parcel_interface_len;
+    const char16_t* parcel_interface = readString16Inplace(&parcel_interface_len);
+    if (len == parcel_interface_len &&
+            (!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) {
         return true;
     } else {
         ALOGW("**** enforceInterface() expected '%s' but read '%s'",
-                String8(interface).string(), String8(str).string());
+              String8(interface, len).string(),
+              String8(parcel_interface, parcel_interface_len).string());
         return false;
     }
 }
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index bdc2e40..4b773e8 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -112,6 +112,10 @@
 {
     sp<IBinder> context = getStrongProxyForHandle(0);
 
+    if (context == nullptr) {
+       ALOGW("Not able to get context object on %s.", mDriverName.c_str());
+    }
+
     // The root object is special since we get it directly from the driver, it is never
     // written by Parcell::writeStrongBinder.
     internal::Stability::tryMarkCompilationUnit(context.get());
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index d4bb85b..4b1a758 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -96,6 +96,9 @@
     // passed in.
     bool                enforceInterface(const String16& interface,
                                          IPCThreadState* threadState = nullptr) const;
+    bool                enforceInterface(const char16_t* interface,
+                                         size_t len,
+                                         IPCThreadState* threadState = nullptr) const;
     bool                checkInterface(IBinder*) const;
 
     void                freeData();
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 703ceae..c33c44f 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -247,15 +247,12 @@
         if (fd != -1) {
             return STATUS_UNKNOWN_ERROR;
         }
-        return parcel->get()->writeInt32(0);  // null
+        return PruneStatusT(parcel->get()->writeInt32(0));  // null
     }
+    status_t status = parcel->get()->writeInt32(1);  // not-null
+    if (status != STATUS_OK) return PruneStatusT(status);
 
-    ParcelFileDescriptor parcelFd = ParcelFileDescriptor(unique_fd(fd));
-    status_t status = parcel->get()->writeParcelable(parcelFd);
-
-    // ownership is retained by caller
-    (void)parcelFd.release().release();
-
+    status = parcel->get()->writeDupParcelFileDescriptor(fd);
     return PruneStatusT(status);
 }
 
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 94ab9f0..e343df7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -30,6 +30,7 @@
 
 #include <private/binder/binder_module.h>
 #include <sys/epoll.h>
+#include <sys/prctl.h>
 
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
@@ -107,6 +108,7 @@
     if (pid == -1)
         return pid;
     if (pid == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
         close(pipefd[0]);
         execv(binderservername, childargv);
         status = -errno;
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index fbc485c..0b77ab3 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -161,6 +161,17 @@
     return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
 }
 
+static std::optional<uint32_t> getPolicyFreqIdx(uint32_t policy) {
+    auto path = StringPrintf("/sys/devices/system/cpu/cpufreq/policy%u/scaling_cur_freq",
+                             gPolicyCpus[policy][0]);
+    auto freqVec = readNumbersFromFile(path);
+    if (!freqVec.has_value() || freqVec->size() != 1) return {};
+    for (uint32_t idx = 0; idx < gPolicyFreqs[policy].size(); ++idx) {
+        if ((*freqVec)[0] == gPolicyFreqs[policy][idx]) return idx + 1;
+    }
+    return {};
+}
+
 // Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes.
 // Returns true on success, false otherwise.
 // Tracking is active only once a live process has successfully called this function; if the calling
@@ -215,7 +226,9 @@
     unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
     if (policyFreqIdxFd < 0) return false;
     for (uint32_t i = 0; i < gNPolicies; ++i) {
-        if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
+        auto freqIdx = getPolicyFreqIdx(i);
+        if (!freqIdx.has_value()) return false;
+        if (writeToMapEntry(policyFreqIdxFd, &i, &(*freqIdx), BPF_ANY)) return false;
     }
 
     gTracking = attachTracepointProgram("sched", "sched_switch") &&
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 90fded0..1b42b69 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -35,7 +35,7 @@
         "/system/bin/mediaserver",
         "/system/bin/netd",
         "/system/bin/sdcard",
-        "/system/bin/statsd",
+        "/apex/com.android.os.statsd/bin/statsd",
         "/system/bin/surfaceflinger",
         "/system/bin/vehicle_network_service",
         "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index acd833f..30e1351 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -101,6 +101,9 @@
         mHeight(height),
         mNextTransaction(nullptr) {
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+    // since the adapter is in the client process, set dequeue timeout
+    // explicitly so that dequeueBuffer will block
+    mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
 
     int8_t disableTripleBuffer = property_get_bool("ro.sf.disable_triple_buffer", 0);
     if (!disableTripleBuffer) {
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index f09decf..fd1793b 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -335,7 +335,3 @@
                                             void* data) {
     return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
 }
-
-int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
-    return query64(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID);
-}
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 02b886c..2d1354c 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -173,25 +173,22 @@
 /**
  * Retrieves how long it took for the last time a buffer was dequeued.
  *
- * \return a negative value on error, otherwise returns the duration in
- * nanoseconds
+ * \return the dequeue duration in nanoseconds
  */
 int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
 
 /**
  * Retrieves how long it took for the last time a buffer was queued.
  *
- * \return a negative value on error, otherwise returns the duration in
- * nanoseconds.
+ * \return the queue duration in nanoseconds
  */
 int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window);
 
 /**
  * Retrieves the system time in nanoseconds when the last time a buffer
- * was dequeued.
+ * started to be dequeued.
  *
- * \return a negative value on error, otherwise returns the duration in
- * nanoseconds.
+ * \return the start time in nanoseconds
  */
 int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window);
 
@@ -200,23 +197,14 @@
  * made by the window will return -ETIMEDOUT after the timeout if the dequeue
  * takes too long.
  *
- * \return NO_ERROR on success, -errno on error.
+ * If the provided timeout is negative, hen this removes the previously configured
+ * timeout. The window then behaves as if ANativeWindow_setDequeueTimeout was
+ * never called.
+ *
+ * \return NO_ERROR on success
+ * \return BAD_VALUE if the dequeue timeout was unabled to be updated, as
+ * updating the dequeue timeout may change internals of the underlying window.
  */
 int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
 
-/**
- * Provides a hint to the window that buffers should be preallocated ahead of
- * time. Note that the window implementation is not guaranteed to preallocate
- * any buffers, for instance if a private API disallows allocation of new
- * buffers. As such no success/error status is returned.
- */
-void ANativeWindow_allocateBuffers(ANativeWindow* window);
-
-/**
- * Retrieves an identifier for the next frame to be queued by this window.
- *
- * \return -errno on error, otherwise returns the next frame id.
- */
-int64_t ANativeWindow_getNextFrameId(ANativeWindow* window);
-
 __END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 0e28fb8..869ca9e 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1051,4 +1051,15 @@
                            outTransformMatrix);
 }
 
+/**
+ * Retrieves an identifier for the next frame to be queued by this window.
+ *
+ * \return the next frame id.
+ */
+static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+    int64_t value;
+    window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value);
+    return value;
+}
+
 __END_DECLS
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index e072e11..1b5d20d 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -25,7 +25,6 @@
     ANativeWindow_getLastDequeueDuration; # apex # introduced=30
     ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
     ANativeWindow_getLastQueueDuration; # apex # introduced=30
-    ANativeWindow_getNextFrameId; # apex # introduced=30
     ANativeWindow_getWidth;
     ANativeWindow_lock;
     ANativeWindow_query; # llndk
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 5c80d55..439d9bf 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Default flags to be used throughout all libraries in inputflinger.
 cc_defaults {
     name: "inputflinger_defaults",
     cflags: [
@@ -23,22 +24,25 @@
     ],
 }
 
-cc_library_shared {
-    name: "libinputflinger",
-    defaults: ["inputflinger_defaults"],
+/////////////////////////////////////////////////
+// libinputflinger
+/////////////////////////////////////////////////
 
+filegroup {
+    name: "libinputflinger_sources",
     srcs: [
         "InputClassifier.cpp",
         "InputClassifierConverter.cpp",
         "InputManager.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputflinger_defaults",
+    srcs: [":libinputflinger_sources"],
     shared_libs: [
         "android.hardware.input.classifier@1.0",
         "libbase",
-        "libinputflinger_base",
-        "libinputreporter",
-        "libinputreader",
         "libbinder",
         "libcrypto",
         "libcutils",
@@ -50,60 +54,76 @@
         "libui",
         "server_configurable_flags",
     ],
+}
 
-    static_libs: [
-        "libinputdispatcher",
+cc_library_shared {
+    name: "libinputflinger",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputflinger_defaults",
     ],
-
     cflags: [
         // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
         //-fvisibility=hidden
     ],
-
+    shared_libs: [
+        // This should consist only of dependencies from inputflinger. Other dependencies should be
+        // in cc_defaults so that they are included in the tests.
+        "libinputflinger_base",
+        "libinputreporter",
+        "libinputreader",
+    ],
+    static_libs: [
+        "libinputdispatcher",
+    ],
+    export_static_lib_headers: [
+        "libinputdispatcher",
+    ],
     export_include_dirs: [
         ".",
         "include",
     ],
-
-    export_static_lib_headers: [
-        "libinputdispatcher",
-    ],
 }
 
+/////////////////////////////////////////////////
+// libinputflinger_base
+/////////////////////////////////////////////////
+
 cc_library_headers {
     name: "libinputflinger_headers",
-    header_libs: ["libinputreporter_headers"],
     export_include_dirs: ["include"],
-    export_header_lib_headers: ["libinputreporter_headers"],
 }
 
-cc_library_shared {
-    name: "libinputflinger_base",
-    defaults: ["inputflinger_defaults"],
-
+filegroup {
+    name: "libinputflinger_base_sources",
     srcs: [
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputflinger_base_defaults",
+    srcs: [":libinputflinger_base_sources"],
     shared_libs: [
         "libbase",
         "libinput",
         "liblog",
         "libutils",
     ],
-
     header_libs: [
         "libinputflinger_headers",
     ],
-
-    export_header_lib_headers: [
-        "libinputflinger_headers",
-    ],
 }
 
-subdirs = [
-    "host",
-    "tests",
-]
+cc_library_shared {
+    name: "libinputflinger_base",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputflinger_base_defaults",
+    ],
+    export_header_lib_headers: [
+        "libinputflinger_headers",
+    ],
+}
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 385b981..066a816 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -7,6 +7,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libinput",
         "libinputflinger_base",
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 3f956a8..a98f4b4 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -12,9 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
-    name: "libinputdispatcher",
-    defaults: ["inputflinger_defaults"],
+cc_library_headers {
+    name: "libinputdispatcher_headers",
+    export_include_dirs: [
+        "include",
+    ],
+}
+
+filegroup {
+    name: "libinputdispatcher_sources",
     srcs: [
         "Connection.cpp",
         "Entry.cpp",
@@ -24,20 +30,41 @@
         "InputState.cpp",
         "InputTarget.cpp",
         "Monitor.cpp",
-        "TouchState.cpp"
+        "TouchState.cpp",
     ],
+}
+
+cc_defaults {
+    name: "libinputdispatcher_defaults",
+    srcs: [":libinputdispatcher_sources"],
     shared_libs: [
         "libbase",
         "libcrypto",
         "libcutils",
         "libinput",
-        "libinputreporter",
-        "libinputflinger_base",
         "liblog",
         "libstatslog",
         "libui",
         "libutils",
     ],
+    header_libs: [
+        "libinputdispatcher_headers",
+    ],
+}
 
-    export_include_dirs: ["include"],
+cc_library_static {
+    name: "libinputdispatcher",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputdispatcher_defaults",
+    ],
+    shared_libs: [
+        // This should consist only of dependencies from inputflinger. Other dependencies should be
+        // in cc_defaults so that they are included in the tests.
+        "libinputreporter",
+        "libinputflinger_base",
+    ],
+    export_header_lib_headers: [
+        "libinputdispatcher_headers",
+    ],
 }
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index b723654..c4b3789 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -57,6 +57,32 @@
     }
     return StringPrintf("%" PRId32, action);
 }
+VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
+    return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
+             entry.displayId},
+            entry.action,
+            entry.downTime,
+            entry.flags & VERIFIED_KEY_EVENT_FLAGS,
+            entry.keyCode,
+            entry.scanCode,
+            entry.metaState,
+            entry.repeatCount};
+}
+
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) {
+    const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+    const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+    const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK;
+    return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source,
+             entry.displayId},
+            rawX,
+            rawY,
+            actionMasked,
+            entry.downTime,
+            entry.flags & VERIFIED_MOTION_EVENT_FLAGS,
+            entry.metaState,
+            entry.buttonState};
+}
 
 // --- EventEntry ---
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index e8c37f0..b5b61cc 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -220,6 +220,9 @@
     static uint32_t nextSeq();
 };
 
+VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry);
+
 class InputDispatcher;
 // A command entry captures state and behavior for an action to be performed in the
 // dispatch loop after the initial processing has taken place.  It is essentially
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 75bc0aa..f9a86dd 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2433,12 +2433,16 @@
         switch (eventEntry->type) {
             case EventEntry::Type::KEY: {
                 KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+                VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(*keyEntry);
+                verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
+                verifiedEvent.action = dispatchEntry->resolvedAction;
+                std::array<uint8_t, 32> hmac = mHmacKeyManager.sign(verifiedEvent);
 
                 // Publish the key event.
                 status = connection->inputPublisher
                                  .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
                                                   keyEntry->source, keyEntry->displayId,
-                                                  INVALID_HMAC, dispatchEntry->resolvedAction,
+                                                  std::move(hmac), dispatchEntry->resolvedAction,
                                                   dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                   keyEntry->scanCode, keyEntry->metaState,
                                                   keyEntry->repeatCount, keyEntry->downTime,
@@ -2482,12 +2486,18 @@
                         usingCoords = scaledCoords;
                     }
                 }
+                VerifiedMotionEvent verifiedEvent =
+                        verifiedMotionEventFromMotionEntry(*motionEntry);
+                verifiedEvent.actionMasked =
+                        dispatchEntry->resolvedAction & AMOTION_EVENT_ACTION_MASK;
+                verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
+                std::array<uint8_t, 32> hmac = mHmacKeyManager.sign(verifiedEvent);
 
                 // Publish the motion event.
                 status = connection->inputPublisher
                                  .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
                                                      motionEntry->source, motionEntry->displayId,
-                                                     INVALID_HMAC, dispatchEntry->resolvedAction,
+                                                     std::move(hmac), dispatchEntry->resolvedAction,
                                                      motionEntry->actionButton,
                                                      dispatchEntry->resolvedFlags,
                                                      motionEntry->edgeFlags, motionEntry->metaState,
@@ -3392,7 +3402,36 @@
 }
 
 std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const InputEvent& event) {
-    return nullptr;
+    std::array<uint8_t, 32> calculatedHmac;
+    std::unique_ptr<VerifiedInputEvent> result;
+    switch (event.getType()) {
+        case AINPUT_EVENT_TYPE_KEY: {
+            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event);
+            VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent);
+            result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent);
+            calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent);
+            break;
+        }
+        case AINPUT_EVENT_TYPE_MOTION: {
+            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(event);
+            VerifiedMotionEvent verifiedMotionEvent =
+                    verifiedMotionEventFromMotionEvent(motionEvent);
+            result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
+            calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+            break;
+        }
+        default: {
+            ALOGE("Cannot verify events of type %" PRId32, event.getType());
+            return nullptr;
+        }
+    }
+    if (calculatedHmac == INVALID_HMAC) {
+        return nullptr;
+    }
+    if (calculatedHmac != event.getHmac()) {
+        return nullptr;
+    }
+    return result;
 }
 
 bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ded59a5..482133e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -217,6 +217,8 @@
     // the pointer stream in order to claim it for a system gesture.
     std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay GUARDED_BY(mLock);
 
+    HmacKeyManager mHmacKeyManager;
+
     // Event injection and synchronization.
     std::condition_variable mInjectionResultAvailable;
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 23c08b2..83a610f 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -21,10 +21,8 @@
     ],
 }
 
-cc_library_shared {
-    name: "libinputreader",
-    defaults: ["inputflinger_defaults"],
-
+filegroup {
+    name: "libinputreader_sources",
     srcs: [
         "EventHub.cpp",
         "InputDevice.cpp",
@@ -44,14 +42,16 @@
         "mapper/TouchInputMapper.cpp",
         "mapper/VibratorInputMapper.cpp",
         "InputReader.cpp",
-        "InputReaderFactory.cpp",
         "TouchVideoDevice.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputreader_defaults",
+    srcs: [":libinputreader_sources"],
     shared_libs: [
         "libbase",
         "libcap",
-        "libinputflinger_base",
         "libcrypto",
         "libcutils",
         "libinput",
@@ -59,13 +59,26 @@
         "libui",
         "libutils",
     ],
-
     header_libs: [
-        "libinputflinger_headers",
         "libinputreader_headers",
     ],
+}
 
+cc_library_shared {
+    name: "libinputreader",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputreader_defaults"
+    ],
+    srcs: [
+        "InputReaderFactory.cpp",
+    ],
+    shared_libs: [
+        // This should consist only of dependencies from inputflinger. Other dependencies should be
+        // in cc_defaults so that they are included in the tests.
+        "libinputflinger_base",
+    ],
     export_header_lib_headers: [
-        "libinputflinger_headers",
+        "libinputreader_headers",
     ],
 }
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ae82cd4..f2afc81 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,6 +18,8 @@
 
 #include "InputDevice.h"
 
+#include <algorithm>
+
 #include "CursorInputMapper.h"
 #include "ExternalStylusInputMapper.h"
 #include "InputReaderContext.h"
@@ -32,25 +34,28 @@
 namespace android {
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
-                         int32_t controllerNumber, const InputDeviceIdentifier& identifier,
-                         uint32_t classes)
+                         const InputDeviceIdentifier& identifier)
       : mContext(context),
         mId(id),
         mGeneration(generation),
-        mControllerNumber(controllerNumber),
+        mControllerNumber(0),
         mIdentifier(identifier),
-        mClasses(classes),
+        mClasses(0),
         mSources(0),
         mIsExternal(false),
         mHasMic(false),
-        mDropUntilNextSync(false) {
-    mDeviceContext = std::make_unique<InputDeviceContext>(*this);
-}
+        mDropUntilNextSync(false) {}
 
 InputDevice::~InputDevice() {}
 
 bool InputDevice::isEnabled() {
-    return mDeviceContext->isDeviceEnabled();
+    if (!hasEventHubDevices()) {
+        return false;
+    }
+    // devices are either all enabled or all disabled, so we only need to check the first
+    auto& devicePair = mDevices.begin()->second;
+    auto& contextPtr = devicePair.first;
+    return contextPtr->isDeviceEnabled();
 }
 
 void InputDevice::setEnabled(bool enabled, nsecs_t when) {
@@ -65,12 +70,15 @@
         return;
     }
 
+    // When resetting some devices, the driver needs to be queried to ensure that a proper reset is
+    // performed. The querying must happen when the device is enabled, so we reset after enabling
+    // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
     if (enabled) {
-        mDeviceContext->enableDevice();
+        for_each_subdevice([](auto& context) { context.enableDevice(); });
         reset(when);
     } else {
         reset(when);
-        mDeviceContext->disableDevice();
+        for_each_subdevice([](auto& context) { context.disableDevice(); });
     }
     // Must change generation to flag this device as changed
     bumpGeneration();
@@ -118,19 +126,18 @@
     for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
 }
 
-void InputDevice::populateMappers() {
-    uint32_t classes = mClasses;
-    std::vector<std::unique_ptr<InputMapper>>& mappers = mMappers;
-    std::unique_ptr<InputDeviceContext>& contextPtr = mDeviceContext;
-
-    // External devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
-        setExternal(true);
+void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
+    if (mDevices.find(eventHubId) != mDevices.end()) {
+        return;
     }
+    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
+    uint32_t classes = contextPtr->getDeviceClasses();
+    std::vector<std::unique_ptr<InputMapper>> mappers;
 
-    // Devices with mics.
-    if (classes & INPUT_DEVICE_CLASS_MIC) {
-        setMic(true);
+    // Check if we should skip population
+    if (!populateMappers) {
+        mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+        return;
     }
 
     // Switch-like devices.
@@ -190,22 +197,58 @@
     if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
         mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
     }
+
+    // insert the context into the devices set
+    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+}
+
+void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+    mDevices.erase(eventHubId);
 }
 
 void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                             uint32_t changes) {
     mSources = 0;
+    mClasses = 0;
+    mControllerNumber = 0;
+
+    for_each_subdevice([this](InputDeviceContext& context) {
+        mClasses |= context.getDeviceClasses();
+        int32_t controllerNumber = context.getDeviceControllerNumber();
+        if (controllerNumber > 0) {
+            if (mControllerNumber && mControllerNumber != controllerNumber) {
+                ALOGW("InputDevice::configure(): composite device contains multiple unique "
+                      "controller numbers");
+            }
+            mControllerNumber = controllerNumber;
+        }
+    });
+
+    mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL);
+    mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC);
 
     if (!isIgnored()) {
         if (!changes) { // first time only
-            mDeviceContext->getConfiguration(&mConfiguration);
+            mConfiguration.clear();
+            for_each_subdevice([this](InputDeviceContext& context) {
+                PropertyMap configuration;
+                context.getConfiguration(&configuration);
+                mConfiguration.addAll(&configuration);
+            });
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
             if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
                 sp<KeyCharacterMap> keyboardLayout =
                         mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
-                if (mDeviceContext->setKeyboardLayoutOverlay(keyboardLayout)) {
+                bool shouldBumpGeneration = false;
+                for_each_subdevice(
+                        [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
+                            if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
+                                shouldBumpGeneration = true;
+                            }
+                        });
+                if (shouldBumpGeneration) {
                     bumpGeneration();
                 }
             }
@@ -313,7 +356,9 @@
             mDropUntilNextSync = true;
             reset(rawEvent->when);
         } else {
-            for_each_mapper([rawEvent](InputMapper& mapper) { mapper.process(rawEvent); });
+            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
+                mapper.process(rawEvent);
+            });
         }
         --count;
     }
@@ -348,16 +393,20 @@
 
 int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
-    for (auto& mapperPtr : mMappers) {
-        InputMapper& mapper = *mapperPtr;
-        if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
-            // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
-            // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
-            int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code);
-            if (currentResult >= AKEY_STATE_DOWN) {
-                return currentResult;
-            } else if (currentResult == AKEY_STATE_UP) {
-                result = currentResult;
+    for (auto& deviceEntry : mDevices) {
+        auto& devicePair = deviceEntry.second;
+        auto& mappers = devicePair.second;
+        for (auto& mapperPtr : mappers) {
+            InputMapper& mapper = *mapperPtr;
+            if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
+                // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+                // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
+                int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code);
+                if (currentResult >= AKEY_STATE_DOWN) {
+                    return currentResult;
+                } else if (currentResult == AKEY_STATE_UP) {
+                    result = currentResult;
+                }
             }
         }
     }
@@ -400,10 +449,6 @@
     for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); });
 }
 
-void InputDevice::fadePointer() {
-    for_each_mapper([](InputMapper& mapper) { mapper.fadePointer(); });
-}
-
 void InputDevice::bumpGeneration() {
     mGeneration = mContext->bumpGeneration();
 }
@@ -424,11 +469,23 @@
             [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); });
 }
 
-InputDeviceContext::InputDeviceContext(InputDevice& device)
+// returns the number of mappers associated with the device
+size_t InputDevice::getMapperCount() {
+    size_t count = 0;
+    for (auto& deviceEntry : mDevices) {
+        auto& devicePair = deviceEntry.second;
+        auto& mappers = devicePair.second;
+        count += mappers.size();
+    }
+    return count;
+}
+
+InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
         mEventHub(device.getContext()->getEventHub()),
-        mId(device.getId()) {}
+        mId(eventHubId),
+        mDeviceId(device.getId()) {}
 
 InputDeviceContext::~InputDeviceContext() {}
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 3e23fa6..6ab5902 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -49,6 +49,7 @@
         mNextSequenceNum(1),
         mGlobalMetaState(0),
         mGeneration(1),
+        mNextInputDeviceId(END_RESERVED_ID),
         mDisableVirtualKeysTimeout(LLONG_MIN),
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
@@ -62,11 +63,7 @@
     } // release lock
 }
 
-InputReader::~InputReader() {
-    for (auto& devicePair : mDevices) {
-        delete devicePair.second;
-    }
-}
+InputReader::~InputReader() {}
 
 status_t InputReader::start() {
     if (mThread) {
@@ -188,29 +185,28 @@
     }
 }
 
-void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
-    if (mDevices.find(deviceId) != mDevices.end()) {
-        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
+    if (mDevices.find(eventHubId) != mDevices.end()) {
+        ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);
         return;
     }
 
-    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
-    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
-    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
-
-    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
+    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
+    std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
     device->configure(when, &mConfig, 0);
     device->reset(when);
 
     if (device->isIgnored()) {
-        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
-              identifier.name.c_str());
+        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
+              "(ignored non-input device)",
+              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
     } else {
-        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(),
+        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",
+              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
               device->getSources());
     }
 
-    mDevices.insert({deviceId, device});
+    mDevices.emplace(eventHubId, device);
     bumpGenerationLocked();
 
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -218,51 +214,68 @@
     }
 }
 
-void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
-    auto deviceIt = mDevices.find(deviceId);
+void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {
+    auto deviceIt = mDevices.find(eventHubId);
     if (deviceIt == mDevices.end()) {
-        ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
+        ALOGW("Ignoring spurious device removed event for eventHubId %d.", eventHubId);
         return;
     }
 
-    InputDevice* device = deviceIt->second;
+    std::shared_ptr<InputDevice> device = std::move(deviceIt->second);
     mDevices.erase(deviceIt);
     bumpGenerationLocked();
 
     if (device->isIgnored()) {
-        ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(),
-              device->getName().c_str());
+        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
+              "(ignored non-input device)",
+              device->getId(), eventHubId, device->getName().c_str(),
+              device->getDescriptor().c_str());
     } else {
-        ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(),
-              device->getName().c_str(), device->getSources());
+        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x",
+              device->getId(), eventHubId, device->getName().c_str(),
+              device->getDescriptor().c_str(), device->getSources());
     }
 
+    device->removeEventHubDevice(eventHubId);
+
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
         notifyExternalStylusPresenceChanged();
     }
 
+    if (device->hasEventHubDevices()) {
+        device->configure(when, &mConfig, 0);
+    }
     device->reset(when);
-    delete device;
 }
 
-InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-                                             const InputDeviceIdentifier& identifier,
-                                             uint32_t classes) {
-    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
-                                          controllerNumber, identifier, classes);
-    device->populateMappers();
+std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
+        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
+    auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
+        return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&
+                devicePair.second->getDescriptor() == identifier.descriptor;
+    });
+
+    std::shared_ptr<InputDevice> device;
+    if (deviceIt != mDevices.end()) {
+        device = deviceIt->second;
+    } else {
+        int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
+        device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
+                                               identifier);
+    }
+    device->addEventHubDevice(eventHubId);
     return device;
 }
 
-void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
+void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                                size_t count) {
-    auto deviceIt = mDevices.find(deviceId);
+    auto deviceIt = mDevices.find(eventHubId);
     if (deviceIt == mDevices.end()) {
-        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
+        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
         return;
     }
 
-    InputDevice* device = deviceIt->second;
+    std::shared_ptr<InputDevice>& device = deviceIt->second;
     if (device->isIgnored()) {
         // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
         return;
@@ -271,15 +284,30 @@
     device->process(rawEvents, count);
 }
 
+InputDevice* InputReader::findInputDevice(int32_t deviceId) {
+    auto deviceIt =
+            std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) {
+                return devicePair.second->getId() == deviceId;
+            });
+    if (deviceIt != mDevices.end()) {
+        return deviceIt->second.get();
+    }
+    return nullptr;
+}
+
 void InputReader::timeoutExpiredLocked(nsecs_t when) {
     for (auto& devicePair : mDevices) {
-        InputDevice* device = devicePair.second;
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         if (!device->isIgnored()) {
             device->timeoutExpired(when);
         }
     }
 }
 
+int32_t InputReader::nextInputDeviceIdLocked() {
+    return ++mNextInputDeviceId;
+}
+
 void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
     // Reset global meta state because it depends on the list of all configured devices.
     updateGlobalMetaStateLocked();
@@ -298,11 +326,15 @@
               InputReaderConfiguration::changesToString(changes).c_str());
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
+        if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
+            updatePointerDisplayLocked();
+        }
+
         if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
             mEventHub->requestReopenDevices();
         } else {
             for (auto& devicePair : mDevices) {
-                InputDevice* device = devicePair.second;
+                std::shared_ptr<InputDevice>& device = devicePair.second;
                 device->configure(now, &mConfig, changes);
             }
         }
@@ -313,7 +345,7 @@
     mGlobalMetaState = 0;
 
     for (auto& devicePair : mDevices) {
-        InputDevice* device = devicePair.second;
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         mGlobalMetaState |= device->getMetaState();
     }
 }
@@ -328,7 +360,7 @@
 
 void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
     for (auto& devicePair : mDevices) {
-        InputDevice* device = devicePair.second;
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -339,7 +371,7 @@
 
 void InputReader::dispatchExternalStylusState(const StylusState& state) {
     for (auto& devicePair : mDevices) {
-        InputDevice* device = devicePair.second;
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         device->updateExternalStylusState(state);
     }
 }
@@ -359,10 +391,43 @@
     }
 }
 
+sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) {
+    sp<PointerControllerInterface> controller = mPointerController.promote();
+    if (controller == nullptr) {
+        controller = mPolicy->obtainPointerController(deviceId);
+        mPointerController = controller;
+        updatePointerDisplayLocked();
+    }
+    return controller;
+}
+
+void InputReader::updatePointerDisplayLocked() {
+    sp<PointerControllerInterface> controller = mPointerController.promote();
+    if (controller == nullptr) {
+        return;
+    }
+
+    std::optional<DisplayViewport> viewport =
+            mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
+    if (!viewport) {
+        ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
+              "mapper. Fall back to default display",
+              mConfig.defaultPointerDisplayId);
+        viewport = mConfig.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
+    }
+    if (!viewport) {
+        ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
+              " PointerController.");
+        return;
+    }
+
+    controller->setDisplayViewport(*viewport);
+}
+
 void InputReader::fadePointerLocked() {
-    for (auto& devicePair : mDevices) {
-        InputDevice* device = devicePair.second;
-        device->fadePointer();
+    sp<PointerControllerInterface> controller = mPointerController.promote();
+    if (controller != nullptr) {
+        controller->fade(PointerControllerInterface::TRANSITION_GRADUAL);
     }
 }
 
@@ -386,7 +451,7 @@
     outInputDevices.clear();
 
     for (auto& devicePair : mDevices) {
-        InputDevice* device = devicePair.second;
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         if (!device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -417,20 +482,17 @@
                                     GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
     if (deviceId >= 0) {
-        auto deviceIt = mDevices.find(deviceId);
-        if (deviceIt != mDevices.end()) {
-            InputDevice* device = deviceIt->second;
-            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result = (device->*getStateFunc)(sourceMask, code);
-            }
+        InputDevice* device = findInputDevice(deviceId);
+        if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+            result = (device->*getStateFunc)(sourceMask, code);
         }
     } else {
         for (auto& devicePair : mDevices) {
-            InputDevice* device = devicePair.second;
+            std::shared_ptr<InputDevice>& device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
                 // value.  Otherwise, return AKEY_STATE_UP as long as one device reports it.
-                int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
+                int32_t currentResult = (device.get()->*getStateFunc)(sourceMask, code);
                 if (currentResult >= AKEY_STATE_DOWN) {
                     return currentResult;
                 } else if (currentResult == AKEY_STATE_UP) {
@@ -443,13 +505,12 @@
 }
 
 void InputReader::toggleCapsLockState(int32_t deviceId) {
-    auto deviceIt = mDevices.find(deviceId);
-    if (deviceIt == mDevices.end()) {
+    InputDevice* device = findInputDevice(deviceId);
+    if (!device) {
         ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
         return;
     }
 
-    InputDevice* device = deviceIt->second;
     if (device->isIgnored()) {
         return;
     }
@@ -470,16 +531,13 @@
                                               uint8_t* outFlags) {
     bool result = false;
     if (deviceId >= 0) {
-        auto deviceIt = mDevices.find(deviceId);
-        if (deviceIt != mDevices.end()) {
-            InputDevice* device = deviceIt->second;
-            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
-            }
+        InputDevice* device = findInputDevice(deviceId);
+        if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+            result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
         }
     } else {
         for (auto& devicePair : mDevices) {
-            InputDevice* device = devicePair.second;
+            std::shared_ptr<InputDevice>& device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
             }
@@ -504,9 +562,8 @@
 void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
                           ssize_t repeat, int32_t token) {
     AutoMutex _l(mLock);
-    auto deviceIt = mDevices.find(deviceId);
-    if (deviceIt != mDevices.end()) {
-        InputDevice* device = deviceIt->second;
+    InputDevice* device = findInputDevice(deviceId);
+    if (device) {
         device->vibrate(pattern, patternSize, repeat, token);
     }
 }
@@ -514,9 +571,8 @@
 void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
     AutoMutex _l(mLock);
 
-    auto deviceIt = mDevices.find(deviceId);
-    if (deviceIt != mDevices.end()) {
-        InputDevice* device = deviceIt->second;
+    InputDevice* device = findInputDevice(deviceId);
+    if (device) {
         device->cancelVibrate(token);
     }
 }
@@ -524,9 +580,8 @@
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
 
-    auto deviceIt = mDevices.find(deviceId);
-    if (deviceIt != mDevices.end()) {
-        InputDevice* device = deviceIt->second;
+    InputDevice* device = findInputDevice(deviceId);
+    if (device) {
         return device->isEnabled();
     }
     ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
@@ -536,13 +591,12 @@
 bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
     AutoMutex _l(mLock);
 
-    auto deviceIt = mDevices.find(deviceId);
-    if (deviceIt == mDevices.end()) {
+    InputDevice* device = findInputDevice(deviceId);
+    if (!device) {
         ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
         return false;
     }
 
-    InputDevice* device = deviceIt->second;
     if (!device->isEnabled()) {
         ALOGW("Ignoring disabled device %s", device->getName().c_str());
         return false;
@@ -571,7 +625,7 @@
     dump += "Input Reader State:\n";
 
     for (const auto& devicePair : mDevices) {
-        InputDevice* const device = devicePair.second;
+        const std::shared_ptr<InputDevice>& device = devicePair.second;
         device->dump(dump);
     }
 
@@ -671,6 +725,11 @@
     mReader->fadePointerLocked();
 }
 
+sp<PointerControllerInterface> InputReader::ContextImpl::getPointerController(int32_t deviceId) {
+    // lock is already held by the input loop
+    return mReader->getPointerControllerLocked(deviceId);
+}
+
 void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
     // lock is already held by the input loop
     mReader->requestTimeoutAtTimeLocked(when);
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 0814d1f..71313fc 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -17,18 +17,19 @@
 #ifndef _UI_INPUTREADER_INPUT_DEVICE_H
 #define _UI_INPUTREADER_INPUT_DEVICE_H
 
+#include <input/DisplayViewport.h>
+#include <input/InputDevice.h>
+#include <stdint.h>
+#include <utils/PropertyMap.h>
+
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
 #include "EventHub.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
 
-#include <input/DisplayViewport.h>
-#include <input/InputDevice.h>
-#include <utils/PropertyMap.h>
-
-#include <stdint.h>
-#include <optional>
-#include <vector>
-
 namespace android {
 
 class InputDeviceContext;
@@ -38,8 +39,7 @@
 class InputDevice {
 public:
     InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
-                int32_t controllerNumber, const InputDeviceIdentifier& identifier,
-                uint32_t classes);
+                const InputDeviceIdentifier& identifier);
     ~InputDevice();
 
     inline InputReaderContext* getContext() { return mContext; }
@@ -50,25 +50,25 @@
     inline const std::string getDescriptor() { return mIdentifier.descriptor; }
     inline uint32_t getClasses() const { return mClasses; }
     inline uint32_t getSources() const { return mSources; }
+    inline bool hasEventHubDevices() const { return !mDevices.empty(); }
 
     inline bool isExternal() { return mIsExternal; }
-    inline void setExternal(bool external) { mIsExternal = external; }
     inline std::optional<uint8_t> getAssociatedDisplayPort() const {
         return mAssociatedDisplayPort;
     }
     inline std::optional<DisplayViewport> getAssociatedViewport() const {
         return mAssociatedViewport;
     }
-    inline void setMic(bool hasMic) { mHasMic = hasMic; }
     inline bool hasMic() const { return mHasMic; }
 
-    inline bool isIgnored() { return mMappers.empty(); }
+    inline bool isIgnored() { return !getMapperCount(); }
 
     bool isEnabled();
     void setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump);
-    void populateMappers();
+    void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
+    void removeEventHubDevice(int32_t eventHubId);
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
     void reset(nsecs_t when);
     void process(const RawEvent* rawEvents, size_t count);
@@ -88,8 +88,6 @@
     int32_t getMetaState();
     void updateMetaState(int32_t keyCode);
 
-    void fadePointer();
-
     void bumpGeneration();
 
     void notifyReset(nsecs_t when);
@@ -99,11 +97,20 @@
 
     std::optional<int32_t> getAssociatedDisplayId();
 
+    size_t getMapperCount();
+
     // construct and add a mapper to the input device
     template <class T, typename... Args>
-    T& addMapper(Args... args) {
-        T* mapper = new T(*mDeviceContext, args...);
-        mMappers.emplace_back(mapper);
+    T& addMapper(int32_t eventHubId, Args... args) {
+        // ensure a device entry exists for this eventHubId
+        addEventHubDevice(eventHubId, false);
+
+        // create mapper
+        auto& devicePair = mDevices[eventHubId];
+        auto& deviceContext = devicePair.first;
+        auto& mappers = devicePair.second;
+        T* mapper = new T(*deviceContext, args...);
+        mappers.emplace_back(mapper);
         return *mapper;
     }
 
@@ -116,8 +123,10 @@
     std::string mAlias;
     uint32_t mClasses;
 
-    std::unique_ptr<InputDeviceContext> mDeviceContext;
-    std::vector<std::unique_ptr<InputMapper>> mMappers;
+    // map from eventHubId to device context and mappers
+    using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
+    using DevicePair = std::pair<std::unique_ptr<InputDeviceContext>, MapperVector>;
+    std::unordered_map<int32_t, DevicePair> mDevices;
 
     uint32_t mSources;
     bool mIsExternal;
@@ -131,10 +140,37 @@
 
     PropertyMap mConfiguration;
 
-    // run a function against every mapper
+    // helpers to interate over the devices collection
+    // run a function against every mapper on every subdevice
     inline void for_each_mapper(std::function<void(InputMapper&)> f) {
-        for (auto& mapperPtr : mMappers) {
-            f(*mapperPtr);
+        for (auto& deviceEntry : mDevices) {
+            auto& devicePair = deviceEntry.second;
+            auto& mappers = devicePair.second;
+            for (auto& mapperPtr : mappers) {
+                f(*mapperPtr);
+            }
+        }
+    }
+
+    // run a function against every mapper on a specific subdevice
+    inline void for_each_mapper_in_subdevice(int32_t eventHubDevice,
+                                             std::function<void(InputMapper&)> f) {
+        auto deviceIt = mDevices.find(eventHubDevice);
+        if (deviceIt != mDevices.end()) {
+            auto& devicePair = deviceIt->second;
+            auto& mappers = devicePair.second;
+            for (auto& mapperPtr : mappers) {
+                f(*mapperPtr);
+            }
+        }
+    }
+
+    // run a function against every subdevice
+    inline void for_each_subdevice(std::function<void(InputDeviceContext&)> f) {
+        for (auto& deviceEntry : mDevices) {
+            auto& devicePair = deviceEntry.second;
+            auto& contextPtr = devicePair.first;
+            f(*contextPtr);
         }
     }
 
@@ -142,10 +178,14 @@
     // if all mappers return nullopt, return nullopt.
     template <typename T>
     inline std::optional<T> first_in_mappers(std::function<std::optional<T>(InputMapper&)> f) {
-        for (auto& mapperPtr : mMappers) {
-            std::optional<T> ret = f(*mapperPtr);
-            if (ret) {
-                return ret;
+        for (auto& deviceEntry : mDevices) {
+            auto& devicePair = deviceEntry.second;
+            auto& mappers = devicePair.second;
+            for (auto& mapperPtr : mappers) {
+                std::optional<T> ret = f(*mapperPtr);
+                if (ret) {
+                    return ret;
+                }
             }
         }
         return std::nullopt;
@@ -159,11 +199,12 @@
  */
 class InputDeviceContext {
 public:
-    InputDeviceContext(InputDevice& device);
+    InputDeviceContext(InputDevice& device, int32_t eventHubId);
     ~InputDeviceContext();
 
     inline InputReaderContext* getContext() { return mContext; }
-    inline int32_t getId() { return mId; }
+    inline int32_t getId() { return mDeviceId; }
+    inline int32_t getEventHubId() { return mId; }
 
     inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); }
     inline InputDeviceIdentifier getDeviceIdentifier() const {
@@ -259,6 +300,7 @@
     InputReaderContext* mContext;
     EventHubInterface* mEventHub;
     int32_t mId;
+    int32_t mDeviceId;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index cf1af04..ca1a081 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -23,6 +23,7 @@
 #include "InputReaderContext.h"
 #include "InputThread.h"
 
+#include <PointerControllerInterface.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 
@@ -84,9 +85,8 @@
 
 protected:
     // These members are protected so they can be instrumented by test cases.
-    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-                                            const InputDeviceIdentifier& identifier,
-                                            uint32_t classes);
+    virtual std::shared_ptr<InputDevice> createDeviceLocked(
+            int32_t deviceId, const InputDeviceIdentifier& identifier);
 
     // With each iteration of the loop, InputReader reads and processes one incoming message from
     // the EventHub.
@@ -103,6 +103,7 @@
         virtual void disableVirtualKeysUntil(nsecs_t time) override;
         virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
         virtual void fadePointer() override;
+        virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) override;
         virtual void requestTimeoutAtTime(nsecs_t when) override;
         virtual int32_t bumpGeneration() override;
         virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
@@ -138,14 +139,16 @@
     static const int EVENT_BUFFER_SIZE = 256;
     RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
 
-    std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices;
+    // An input device can represent a collection of EventHub devices. This map provides a way
+    // to lookup the input device instance from the EventHub device id.
+    std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices;
 
     // low-level input event decoding and device management
     void processEventsLocked(const RawEvent* rawEvents, size_t count);
 
-    void addDeviceLocked(nsecs_t when, int32_t deviceId);
-    void removeDeviceLocked(nsecs_t when, int32_t deviceId);
-    void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
+    void addDeviceLocked(nsecs_t when, int32_t eventHubId);
+    void removeDeviceLocked(nsecs_t when, int32_t eventHubId);
+    void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count);
     void timeoutExpiredLocked(nsecs_t when);
 
     void handleConfigurationChangedLocked(nsecs_t when);
@@ -158,11 +161,18 @@
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
     void dispatchExternalStylusState(const StylusState& state);
 
+    // The PointerController that is shared among all the input devices that need it.
+    wp<PointerControllerInterface> mPointerController;
+    sp<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
+    void updatePointerDisplayLocked();
     void fadePointerLocked();
 
     int32_t mGeneration;
     int32_t bumpGenerationLocked();
 
+    int32_t mNextInputDeviceId;
+    int32_t nextInputDeviceIdLocked();
+
     void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
 
     nsecs_t mDisableVirtualKeysTimeout;
@@ -181,6 +191,9 @@
                            GetStateFunc getStateFunc);
     bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
                                      const int32_t* keyCodes, uint8_t* outFlags);
+
+    // find an InputDevice from an InputDevice id
+    InputDevice* findInputDevice(int32_t deviceId);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index e14fbbe..d5527cf 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -28,6 +28,7 @@
 class InputListenerInterface;
 class InputMapper;
 class InputReaderPolicyInterface;
+class PointerControllerInterface;
 struct StylusState;
 
 /* Internal interface used by individual input devices to access global input device state
@@ -45,6 +46,7 @@
     virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0;
 
     virtual void fadePointer() = 0;
+    virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
 
     virtual void requestTimeoutAtTime(nsecs_t when) = 0;
     virtual int32_t bumpGeneration() = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 78f3382..c4162bc 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "CursorInputMapper.h"
 
@@ -132,7 +132,7 @@
                 mYPrecision = 1.0f;
                 mXScale = 1.0f;
                 mYScale = 1.0f;
-                mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+                mPointerController = getContext()->getPointerController(getDeviceId());
                 break;
             case Parameters::MODE_NAVIGATION:
                 mSource = AINPUT_SOURCE_TRACKBALL;
@@ -189,34 +189,10 @@
             }
         }
 
-        // Update the PointerController if viewports changed.
-        if (mParameters.mode == Parameters::MODE_POINTER) {
-            updatePointerControllerDisplayViewport(*config);
-        }
         bumpGeneration();
     }
 }
 
-void CursorInputMapper::updatePointerControllerDisplayViewport(
-        const InputReaderConfiguration& config) {
-    std::optional<DisplayViewport> viewport =
-            config.getDisplayViewportById(config.defaultPointerDisplayId);
-    if (!viewport) {
-        ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
-              "mapper. Fall back to default display",
-              config.defaultPointerDisplayId);
-        viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
-    }
-
-    if (!viewport) {
-        ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
-              " PointerController.");
-        return;
-    }
-
-    mPointerController->setDisplayViewport(*viewport);
-}
-
 void CursorInputMapper::configureParameters() {
     mParameters.mode = Parameters::MODE_POINTER;
     String8 cursorModeString;
@@ -481,12 +457,6 @@
     }
 }
 
-void CursorInputMapper::fadePointer() {
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-    }
-}
-
 std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mParameters.mode == Parameters::MODE_POINTER) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 94ab306..f65ac39 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -66,8 +66,6 @@
 
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
 
-    virtual void fadePointer() override;
-
     virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 private:
@@ -117,7 +115,6 @@
     void dumpParameters(std::string& dump);
 
     void sync(nsecs_t when);
-    void updatePointerControllerDisplayViewport(const InputReaderConfiguration& config);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 37e4047..37d1d74 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "ExternalStylusInputMapper.h"
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 92af612..a8fe39a 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "InputMapper.h"
 
@@ -71,8 +71,6 @@
 
 void InputMapper::updateExternalStylusState(const StylusState& state) {}
 
-void InputMapper::fadePointer() {}
-
 status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
     return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
 }
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 09888bf..949c7ea 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -71,7 +71,6 @@
 
     virtual void updateExternalStylusState(const StylusState& state);
 
-    virtual void fadePointer();
     virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
 
 protected:
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 57c85b6..7c3b1d9 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "JoystickInputMapper.h"
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 9ab707f..ab354a2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "KeyboardInputMapper.h"
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index d195a07..d77c8c8 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "MultiTouchInputMapper.h"
 
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index a1cce56..ed93f14 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "RotaryEncoderInputMapper.h"
 
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 52b2449..e79aeb2 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "SwitchInputMapper.h"
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index e832804..7543374 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "TouchInputMapper.h"
 
@@ -756,16 +756,11 @@
         mOrientedRanges.clear();
     }
 
-    // Create or update pointer controller if needed.
+    // Create pointer controller if needed.
     if (mDeviceMode == DEVICE_MODE_POINTER ||
         (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
-        if (mPointerController == nullptr || viewportChanged) {
-            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-            // Set the DisplayViewport for the PointerController to the default pointer display
-            // that is recommended by the configuration before using it.
-            std::optional<DisplayViewport> defaultViewport =
-                    mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
-            mPointerController->setDisplayViewport(defaultViewport.value_or(mViewport));
+        if (mPointerController == nullptr) {
+            mPointerController = getContext()->getPointerController(getDeviceId());
         }
     } else {
         mPointerController.clear();
@@ -3624,12 +3619,6 @@
     return changed;
 }
 
-void TouchInputMapper::fadePointer() {
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-    }
-}
-
 void TouchInputMapper::cancelTouch(nsecs_t when) {
     abortPointerUsage(when, 0 /*policyFlags*/);
     abortTouches(when, 0 /* policyFlags*/);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 3a61206..e21a33a 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -148,7 +148,6 @@
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                        const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual void fadePointer() override;
     virtual void cancelTouch(nsecs_t when) override;
     virtual void timeoutExpired(nsecs_t when) override;
     virtual void updateExternalStylusState(const StylusState& state) override;
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 1b584ea..7665680 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "VibratorInputMapper.h"
 
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index 5956fb0..fbc51da 100644
--- a/services/inputflinger/reporter/Android.bp
+++ b/services/inputflinger/reporter/Android.bp
@@ -17,25 +17,33 @@
     export_include_dirs: ["."],
 }
 
-cc_library_shared {
-    name: "libinputreporter",
-    defaults: ["inputflinger_defaults"],
-
+filegroup {
+    name: "libinputreporter_sources",
     srcs: [
-        "InputReporter.cpp",
+            "InputReporter.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputreporter_defaults",
+    srcs: [":libinputreporter_sources"],
     shared_libs: [
         "liblog",
         "libutils",
     ],
-
     header_libs: [
-        "libinputflinger_headers",
+        "libinputreporter_headers",
     ],
+}
 
+cc_library_shared {
+    name: "libinputreporter",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputreporter_defaults",
+    ],
     export_header_lib_headers: [
-        "libinputflinger_headers",
+        "libinputreporter_headers",
     ],
 }
 
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index f913d82..73d2272 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -1,7 +1,31 @@
-// Build the unit tests.
+// 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.
 
 cc_test {
     name: "inputflinger_tests",
+    defaults: [
+        "inputflinger_defaults",
+        // For all targets inside inputflinger, these tests build all of their sources using their
+        // defaults rather than including them as shared or static libraries. By doing so, the tests
+        // will always run against the compiled version of the inputflinger code rather than the
+        // version on the device.
+        "libinputflinger_base_defaults",
+        "libinputreader_defaults",
+        "libinputreporter_defaults",
+        "libinputdispatcher_defaults",
+        "libinputflinger_defaults",
+    ],
     srcs: [
         "BlockingQueue_test.cpp",
         "EventHub_test.cpp",
@@ -12,31 +36,5 @@
         "InputReader_test.cpp",
         "UinputDevice.cpp",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-Wno-unused-parameter",
-        "-Wthread-safety",
-    ],
-    shared_libs: [
-        "android.hardware.input.classifier@1.0",
-        "libbase",
-        "libbinder",
-        "libcutils",
-        "liblog",
-        "libutils",
-        "libhardware",
-        "libhardware_legacy",
-        "libui",
-        "libinput",
-        "libinputflinger",
-        "libinputreader",
-        "libinputflinger_base",
-        "libinputservice",
-    ],
-    header_libs: [
-        "libinputreader_headers",
-    ],
     require_root: true,
 }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c4092cd..2fb1b65 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1398,6 +1398,44 @@
     window->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocus(true);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+    mDispatcher->notifyKey(&keyArgs);
+
+    InputEvent* event = window->consume();
+    ASSERT_NE(event, nullptr);
+
+    std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+    ASSERT_NE(verified, nullptr);
+    ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY);
+
+    ASSERT_EQ(keyArgs.eventTime, verified->eventTimeNanos);
+    ASSERT_EQ(keyArgs.deviceId, verified->deviceId);
+    ASSERT_EQ(keyArgs.source, verified->source);
+    ASSERT_EQ(keyArgs.displayId, verified->displayId);
+
+    const VerifiedKeyEvent& verifiedKey = static_cast<const VerifiedKeyEvent&>(*verified);
+
+    ASSERT_EQ(keyArgs.action, verifiedKey.action);
+    ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
+    ASSERT_EQ(keyArgs.eventTime, verifiedKey.eventTimeNanos);
+    ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
+    ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
+    ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
+    ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
+    ASSERT_EQ(0, verifiedKey.repeatCount);
+}
+
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 01bd9db..4da9d93 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -18,12 +18,15 @@
 #include <InputDevice.h>
 #include <InputMapper.h>
 #include <InputReader.h>
+#include <InputReaderBase.h>
+#include <InputReaderFactory.h>
 #include <KeyboardInputMapper.h>
 #include <MultiTouchInputMapper.h>
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
 #include <TestInputListener.h>
 #include <TouchInputMapper.h>
+#include <UinputDevice.h>
 
 #include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
@@ -187,17 +190,19 @@
     }
 
     void assertInputDevicesChanged() {
-        std::unique_lock<std::mutex> lock(mLock);
-        base::ScopedLockAssertion assumeLocked(mLock);
+        waitForInputDevices([](bool devicesChanged) {
+            if (!devicesChanged) {
+                FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+            }
+        });
+    }
 
-        const bool devicesChanged =
-                mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
-                    return mInputDevicesChanged;
-                });
-        if (!devicesChanged) {
-            FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
-        }
-        mInputDevicesChanged = false;
+    void assertInputDevicesNotChanged() {
+        waitForInputDevices([](bool devicesChanged) {
+            if (devicesChanged) {
+                FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+            }
+        });
     }
 
     virtual void clearViewports() {
@@ -331,6 +336,18 @@
     virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
         return "";
     }
+
+    void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+
+        const bool devicesChanged =
+                mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mInputDevicesChanged;
+                });
+        ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
+        mInputDevicesChanged = false;
+    }
 };
 
 // --- FakeEventHub ---
@@ -830,6 +847,7 @@
     bool mUpdateGlobalMetaStateWasCalled;
     int32_t mGeneration;
     uint32_t mNextSequenceNum;
+    wp<PointerControllerInterface> mPointerController;
 
 public:
     FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
@@ -857,6 +875,18 @@
         return mGeneration;
     }
 
+    void updatePointerDisplay() {
+        sp<PointerControllerInterface> controller = mPointerController.promote();
+        if (controller != nullptr) {
+            InputReaderConfiguration config;
+            mPolicy->getReaderConfiguration(&config);
+            auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId);
+            if (viewport) {
+                controller->setDisplayViewport(*viewport);
+            }
+        }
+    }
+
 private:
     virtual void updateGlobalMetaState() {
         mUpdateGlobalMetaStateWasCalled = true;
@@ -883,6 +913,16 @@
 
     virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
 
+    virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) {
+        sp<PointerControllerInterface> controller = mPointerController.promote();
+        if (controller == nullptr) {
+            controller = mPolicy->obtainPointerController(deviceId);
+            mPointerController = controller;
+            updatePointerDisplay();
+        }
+        return controller;
+    }
+
     virtual void fadePointer() {
     }
 
@@ -1091,7 +1131,7 @@
 // --- InstrumentedInputReader ---
 
 class InstrumentedInputReader : public InputReader {
-    InputDevice* mNextDevice;
+    std::shared_ptr<InputDevice> mNextDevice;
 
 public:
     InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -1099,37 +1139,31 @@
                             const sp<InputListenerInterface>& listener)
           : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
 
-    virtual ~InstrumentedInputReader() {
-        if (mNextDevice) {
-            delete mNextDevice;
-        }
-    }
+    virtual ~InstrumentedInputReader() {}
 
-    void setNextDevice(InputDevice* device) { mNextDevice = device; }
+    void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; }
 
-    InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
-                           uint32_t classes, const std::string& location = "") {
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location = "") {
         InputDeviceIdentifier identifier;
         identifier.name = name;
         identifier.location = location;
         int32_t generation = deviceId + 1;
-        return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
-                               classes);
+        return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier);
     }
 
     // Make the protected loopOnce method accessible to tests.
     using InputReader::loopOnce;
 
 protected:
-    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-                                            const InputDeviceIdentifier& identifier,
-                                            uint32_t classes) {
+    virtual std::shared_ptr<InputDevice> createDeviceLocked(
+            int32_t eventHubId, const InputDeviceIdentifier& identifier) {
         if (mNextDevice) {
-            InputDevice* device = mNextDevice;
+            std::shared_ptr<InputDevice> device(mNextDevice);
             mNextDevice = nullptr;
             return device;
         }
-        return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes);
+        return InputReader::createDeviceLocked(eventHubId, identifier);
     }
 
     friend class InputReaderTest;
@@ -1340,12 +1374,12 @@
         mFakePolicy.clear();
     }
 
-    void addDevice(int32_t deviceId, const std::string& name, uint32_t classes,
-            const PropertyMap* configuration) {
-        mFakeEventHub->addDevice(deviceId, name, classes);
+    void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes,
+                   const PropertyMap* configuration) {
+        mFakeEventHub->addDevice(eventHubId, name, classes);
 
         if (configuration) {
-            mFakeEventHub->addConfigurationMap(deviceId, configuration);
+            mFakeEventHub->addConfigurationMap(eventHubId, configuration);
         }
         mFakeEventHub->finishDeviceScan();
         mReader->loopOnce();
@@ -1364,14 +1398,14 @@
         mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
+    FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
                                                   const std::string& name, uint32_t classes,
                                                   uint32_t sources,
                                                   const PropertyMap* configuration) {
-        InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes);
-        FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(sources);
+        std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
+        FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
         mReader->setNextDevice(device);
-        addDevice(deviceId, name, classes, configuration);
+        addDevice(eventHubId, name, classes, configuration);
         return mapper;
     }
 };
@@ -1385,7 +1419,7 @@
     std::vector<InputDeviceInfo> inputDevices;
     mReader->getInputDevices(inputDevices);
     ASSERT_EQ(1U, inputDevices.size());
-    ASSERT_EQ(1, inputDevices[0].getId());
+    ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
     ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
@@ -1394,7 +1428,7 @@
     // Should also have received a notification describing the new input devices.
     inputDevices = mFakePolicy->getInputDevices();
     ASSERT_EQ(1U, inputDevices.size());
-    ASSERT_EQ(1, inputDevices[0].getId());
+    ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
     ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
@@ -1402,13 +1436,14 @@
 }
 
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
-    constexpr int32_t deviceId = 1;
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
+    constexpr int32_t eventHubId = 1;
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
-    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
 
@@ -1438,8 +1473,11 @@
 }
 
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
-            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
                                          AINPUT_SOURCE_KEYBOARD, nullptr);
     mapper.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
 
@@ -1447,13 +1485,16 @@
             AINPUT_SOURCE_ANY, AKEYCODE_A))
             << "Should return unknown when the device id is >= 0 but unknown.";
 
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1,
-            AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
-            << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+            << "Should return unknown when the device id is valid but the sources are not "
+               "supported by the device.";
 
-    ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1,
-            AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL,
+                                       AKEYCODE_A))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1,
             AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
@@ -1465,8 +1506,11 @@
 }
 
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
-            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
                                          AINPUT_SOURCE_KEYBOARD, nullptr);
     mapper.setScanCodeState(KEY_A, AKEY_STATE_DOWN);
 
@@ -1474,13 +1518,16 @@
             AINPUT_SOURCE_ANY, KEY_A))
             << "Should return unknown when the device id is >= 0 but unknown.";
 
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1,
-            AINPUT_SOURCE_TRACKBALL, KEY_A))
-            << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getScanCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, KEY_A))
+            << "Should return unknown when the device id is valid but the sources are not "
+               "supported by the device.";
 
-    ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1,
-            AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getScanCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL,
+                                        KEY_A))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1,
             AINPUT_SOURCE_TRACKBALL, KEY_A))
@@ -1492,8 +1539,11 @@
 }
 
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
-            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
                                          AINPUT_SOURCE_KEYBOARD, nullptr);
     mapper.setSwitchState(SW_LID, AKEY_STATE_DOWN);
 
@@ -1501,13 +1551,16 @@
             AINPUT_SOURCE_ANY, SW_LID))
             << "Should return unknown when the device id is >= 0 but unknown.";
 
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1,
-            AINPUT_SOURCE_TRACKBALL, SW_LID))
-            << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getSwitchState(deviceId, AINPUT_SOURCE_TRACKBALL, SW_LID))
+            << "Should return unknown when the device id is valid but the sources are not "
+               "supported by the device.";
 
-    ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1,
-            AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getSwitchState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL,
+                                      SW_LID))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1,
             AINPUT_SOURCE_TRACKBALL, SW_LID))
@@ -1519,8 +1572,11 @@
 }
 
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
-            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
                                          AINPUT_SOURCE_KEYBOARD, nullptr);
 
     mapper.addSupportedKeyCode(AKEYCODE_A);
@@ -1534,13 +1590,16 @@
     ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
 
     flags[3] = 1;
-    ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
-            << "Should return false when device id is valid but the sources are not supported by the device.";
+    ASSERT_FALSE(mReader->hasKeys(deviceId, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+            << "Should return false when device id is valid but the sources are not supported by "
+               "the device.";
     ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
 
     flags[3] = 1;
-    ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_TRUE(mReader->hasKeys(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4,
+                                 keyCodes, flags))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
     ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
 
     flags[3] = 1;
@@ -1555,7 +1614,8 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
-    addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
+    constexpr int32_t eventHubId = 1;
+    addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
 
     NotifyConfigurationChangedArgs args;
 
@@ -1564,31 +1624,35 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     FakeInputMapper& mapper =
-            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
                                          AINPUT_SOURCE_KEYBOARD, nullptr);
 
-    mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
+    mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
     ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
     ASSERT_EQ(0, event.when);
-    ASSERT_EQ(1, event.deviceId);
+    ASSERT_EQ(eventHubId, event.deviceId);
     ASSERT_EQ(EV_KEY, event.type);
     ASSERT_EQ(KEY_A, event.code);
     ASSERT_EQ(1, event.value);
 }
 
 TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
-    constexpr int32_t deviceId = 1;
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
+    constexpr int32_t eventHubId = 1;
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    device->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
-    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1614,12 +1678,13 @@
 }
 
 TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
-    constexpr int32_t deviceId = 1;
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "USB1";
-    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
-            DEVICE_LOCATION);
-    FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakeInputMapper& mapper =
+            device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
     mReader->setNextDevice(device);
 
     const uint8_t hdmi1 = 1;
@@ -1639,7 +1704,7 @@
     // Add the device, and make sure all of the callbacks are triggered.
     // The device is added after the input port associations are processed since
     // we do not yet support dynamic device-to-display associations.
-    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
@@ -1655,6 +1720,119 @@
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
+// --- InputReaderIntegrationTest ---
+
+// These tests create and interact with the InputReader only through its interface.
+// The InputReader is started during SetUp(), which starts its processing in its own
+// thread. The tests use linux uinput to emulate input devices.
+// NOTE: Interacting with the physical device while these tests are running may cause
+// the tests to fail.
+class InputReaderIntegrationTest : public testing::Test {
+protected:
+    sp<TestInputListener> mTestListener;
+    sp<FakeInputReaderPolicy> mFakePolicy;
+    sp<InputReaderInterface> mReader;
+
+    virtual void SetUp() override {
+        mFakePolicy = new FakeInputReaderPolicy();
+        mTestListener = new TestInputListener();
+
+        mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
+        ASSERT_EQ(mReader->start(), OK);
+
+        // Since this test is run on a real device, all the input devices connected
+        // to the test device will show up in mReader. We wait for those input devices to
+        // show up before beginning the tests.
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    }
+
+    virtual void TearDown() override {
+        ASSERT_EQ(mReader->stop(), OK);
+        mTestListener.clear();
+        mFakePolicy.clear();
+    }
+};
+
+TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
+    // An invalid input device that is only used for this test.
+    class InvalidUinputDevice : public UinputDevice {
+    public:
+        InvalidUinputDevice() : UinputDevice("Invalid Device") {}
+
+    private:
+        void configureDevice(int fd, uinput_user_dev* device) override {}
+    };
+
+    const size_t numDevices = mFakePolicy->getInputDevices().size();
+
+    // UinputDevice does not set any event or key bits, so InputReader should not
+    // consider it as a valid device.
+    std::unique_ptr<UinputDevice> invalidDevice = createUinputDevice<InvalidUinputDevice>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled());
+    ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
+
+    invalidDevice.reset();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled());
+    ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderIntegrationTest, AddNewDevice) {
+    const size_t initialNumDevices = mFakePolicy->getInputDevices().size();
+
+    std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
+
+    // Find the test device by its name.
+    std::vector<InputDeviceInfo> inputDevices;
+    mReader->getInputDevices(inputDevices);
+    InputDeviceInfo* keyboardInfo = nullptr;
+    const char* keyboardName = keyboard->getName();
+    for (unsigned int i = 0; i < initialNumDevices + 1; i++) {
+        if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) {
+            keyboardInfo = &inputDevices[i];
+            break;
+        }
+    }
+    ASSERT_NE(keyboardInfo, nullptr);
+    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources());
+    ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size());
+
+    keyboard.reset();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_EQ(initialNumDevices, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) {
+    std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+    NotifyConfigurationChangedArgs configChangedArgs;
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs));
+    uint32_t prevSequenceNum = configChangedArgs.sequenceNum;
+    nsecs_t prevTimestamp = configChangedArgs.eventTime;
+
+    NotifyKeyArgs keyArgs;
+    keyboard->pressAndReleaseHomeKey();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_LT(prevSequenceNum, keyArgs.sequenceNum);
+    prevSequenceNum = keyArgs.sequenceNum;
+    ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+    prevTimestamp = keyArgs.eventTime;
+
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_LT(prevSequenceNum, keyArgs.sequenceNum);
+    ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+}
 
 // --- InputDeviceTest ---
 class InputDeviceTest : public testing::Test {
@@ -1665,13 +1843,14 @@
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const uint32_t DEVICE_CLASSES;
+    static const int32_t EVENTHUB_ID;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
 
-    InputDevice* mDevice;
+    std::shared_ptr<InputDevice> mDevice;
 
     virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
@@ -1679,17 +1858,16 @@
         mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
-        mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
+        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
-                DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+        mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+                                                identifier);
     }
 
     virtual void TearDown() override {
-        delete mDevice;
-
+        mDevice = nullptr;
         delete mFakeContext;
         mFakeListener.clear();
         mFakePolicy.clear();
@@ -1698,20 +1876,21 @@
 
 const char* InputDeviceTest::DEVICE_NAME = "device";
 const char* InputDeviceTest::DEVICE_LOCATION = "USB1";
-const int32_t InputDeviceTest::DEVICE_ID = 1;
+const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000;
 const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
 const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
 const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
         | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
+const int32_t InputDeviceTest::EVENTHUB_ID = 1;
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
     ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
-    ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
+    ASSERT_EQ(0U, mDevice->getClasses());
 }
 
-TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsTrue) {
-    ASSERT_EQ(mDevice->isEnabled(), true);
+TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
+    ASSERT_EQ(mDevice->isEnabled(), false);
 }
 
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
@@ -1758,9 +1937,10 @@
 
 TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
     // Configuration.
-    mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value"));
 
-    FakeInputMapper& mapper1 = mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
+    FakeInputMapper& mapper1 =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
     mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     mapper1.setMetaState(AMETA_ALT_ON);
     mapper1.addSupportedKeyCode(AKEYCODE_A);
@@ -1771,7 +1951,8 @@
     mapper1.setScanCodeState(3, AKEY_STATE_UP);
     mapper1.setSwitchState(4, AKEY_STATE_DOWN);
 
-    FakeInputMapper& mapper2 = mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
+    FakeInputMapper& mapper2 =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
     mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
@@ -1842,6 +2023,7 @@
 
     // Event handling.
     RawEvent event;
+    event.deviceId = EVENTHUB_ID;
     mDevice->process(&event, 1);
 
     ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
@@ -1852,7 +2034,7 @@
 // 1. Device is disabled if the viewport corresponding to the associated display is not found
 // 2. Device is disabled when setEnabled API is called
 TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
-    mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
 
     // First Configuration.
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
@@ -1901,6 +2083,7 @@
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const uint32_t DEVICE_CLASSES;
+    static const int32_t EVENTHUB_ID;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -1908,7 +2091,7 @@
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
-    virtual void SetUp() override {
+    virtual void SetUp(uint32_t classes) {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1916,12 +2099,13 @@
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
-                DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier);
 
-        mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
+        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes);
     }
 
+    virtual void SetUp() override { SetUp(DEVICE_CLASSES); }
+
     virtual void TearDown() override {
         delete mDevice;
         delete mFakeContext;
@@ -1930,16 +2114,19 @@
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
+        mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value));
     }
 
     void configureDevice(uint32_t changes) {
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            mFakeContext->updatePointerDisplay();
+        }
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
     template <class T, typename... Args>
     T& addMapperAndConfigure(Args... args) {
-        T& mapper = mDevice->addMapper<T>(args...);
+        T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
         configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
         return mapper;
@@ -1961,7 +2148,7 @@
                         int32_t value) {
         RawEvent event;
         event.when = when;
-        event.deviceId = mapper.getDeviceId();
+        event.deviceId = mapper.getDeviceContext().getEventHubId();
         event.type = type;
         event.code = code;
         event.value = value;
@@ -2006,11 +2193,11 @@
 
 const char* InputMapperTest::DEVICE_NAME = "device";
 const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = 1;
+const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
 const int32_t InputMapperTest::DEVICE_GENERATION = 2;
 const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
 const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
-
+const int32_t InputMapperTest::EVENTHUB_ID = 1;
 
 // --- SwitchInputMapperTest ---
 
@@ -2027,10 +2214,10 @@
 TEST_F(SwitchInputMapperTest, GetSwitchState) {
     SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
-    mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1);
+    mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1);
     ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 
-    mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0);
+    mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0);
     ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 }
 
@@ -2104,8 +2291,8 @@
 TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
     const int32_t USAGE_A = 0x070004;
     const int32_t USAGE_UNKNOWN = 0x07ffff;
-    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-    mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
@@ -2202,8 +2389,8 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
@@ -2241,10 +2428,10 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
@@ -2262,10 +2449,10 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
     addConfigurationProperty("keyboard.orientationAware", "1");
     KeyboardInputMapper& mapper =
@@ -2338,7 +2525,7 @@
 TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) {
     // If the keyboard is not orientation aware,
     // key events should not be associated with a specific display id
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
@@ -2363,7 +2550,7 @@
 TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
     // If the keyboard is orientation aware,
     // key events should be associated with the internal viewport
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
     addConfigurationProperty("keyboard.orientationAware", "1");
     KeyboardInputMapper& mapper =
@@ -2398,10 +2585,10 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1);
+    mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1);
     ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
-    mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0);
+    mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0);
     ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 }
 
@@ -2410,10 +2597,10 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1);
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1);
     ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
-    mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0);
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0);
     ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 }
 
@@ -2422,7 +2609,7 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
 
     const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
     uint8_t flags[2] = { 0, 0 };
@@ -2432,99 +2619,100 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
-    mFakeEventHub->addLed(DEVICE_ID, LED_CAPSL, true /*initially on*/);
-    mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/);
-    mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initialization should have turned all of the lights off.
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
 
     // Toggle caps lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle caps lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
 TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
     // keyboard 1.
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
     // keyboard 2.
     const std::string USB2 = "USB2";
     constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
     InputDeviceIdentifier identifier;
     identifier.name = "KEYBOARD2";
     identifier.location = USB2;
     std::unique_ptr<InputDevice> device2 =
             std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-                                          DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
-    mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
-    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
-    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+                                          identifier);
+    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     KeyboardInputMapper& mapper2 =
-            device2->addMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
@@ -2576,14 +2764,23 @@
                                                 AKEYCODE_DPAD_LEFT, newDisplayId));
 }
 
-TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) {
+// --- KeyboardInputMapperTest_ExternalDevice ---
+
+class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
+protected:
+    virtual void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
+    }
+};
+
+TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
     // For external devices, non-media keys will trigger wake on key down. Media keys need to be
     // marked as WAKE in the keylayout file to trigger wake.
-    mDevice->setExternal(true);
 
-    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
+                          POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
@@ -2615,13 +2812,12 @@
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 }
 
-TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) {
+TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) {
     // Tv Remote key's wake behavior is prescribed by the keylayout file.
-    mDevice->setExternal(true);
 
-    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
 
     addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
     KeyboardInputMapper& mapper =
@@ -3563,10 +3759,10 @@
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
-    mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
-    mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE);
+    mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[0]);
+    mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[1]);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE);
 }
 
 void TouchInputMapperTest::prepareLocationCalibration() {
@@ -3627,33 +3823,29 @@
 };
 
 void SingleTouchInputMapperTest::prepareButtons() {
-    mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
 }
 
 void SingleTouchInputMapperTest::prepareAxes(int axes) {
     if (axes & POSITION) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X,
-                RAW_X_MIN, RAW_X_MAX, 0, 0);
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_Y,
-                RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
     }
     if (axes & PRESSURE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_PRESSURE,
-                RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MIN,
+                                       RAW_PRESSURE_MAX, 0, 0);
     }
     if (axes & TOOL) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH,
-                RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0,
+                                       0);
     }
     if (axes & DISTANCE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_DISTANCE,
-                RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_DISTANCE, RAW_DISTANCE_MIN,
+                                       RAW_DISTANCE_MAX, 0, 0);
     }
     if (axes & TILT) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_X,
-                RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_Y,
-                RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_X, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_Y, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
     }
 }
 
@@ -3709,8 +3901,8 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
-    mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X);
-    mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y);
+    mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_X);
+    mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_Y);
     prepareButtons();
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -4777,7 +4969,7 @@
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
@@ -4939,51 +5131,47 @@
 
 void MultiTouchInputMapperTest::prepareAxes(int axes) {
     if (axes & POSITION) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_X,
-                RAW_X_MIN, RAW_X_MAX, 0, 0);
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_Y,
-                RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
     }
     if (axes & TOUCH) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR,
-                RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN,
+                                       RAW_TOUCH_MAX, 0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR,
-                    RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+            mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, RAW_TOUCH_MIN,
+                                           RAW_TOUCH_MAX, 0, 0);
         }
     }
     if (axes & TOOL) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR,
-                RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX,
+                                       0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR,
-                    RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0);
+            mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MAX,
+                                           RAW_TOOL_MAX, 0, 0);
         }
     }
     if (axes & ORIENTATION) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_ORIENTATION,
-                RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, RAW_ORIENTATION_MIN,
+                                       RAW_ORIENTATION_MAX, 0, 0);
     }
     if (axes & PRESSURE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE,
-                RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, RAW_PRESSURE_MIN,
+                                       RAW_PRESSURE_MAX, 0, 0);
     }
     if (axes & DISTANCE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_DISTANCE,
-                RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_DISTANCE, RAW_DISTANCE_MIN,
+                                       RAW_DISTANCE_MAX, 0, 0);
     }
     if (axes & ID) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
-                RAW_ID_MIN, RAW_ID_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, 0,
+                                       0);
     }
     if (axes & SLOT) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_SLOT,
-                RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0);
-        mFakeEventHub->setAbsoluteAxisValue(DEVICE_ID, ABS_MT_SLOT, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0);
+        mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, 0);
     }
     if (axes & TOOL_TYPE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOOL_TYPE,
-                0, MT_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
     }
 }
 
@@ -6272,7 +6460,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
@@ -6451,35 +6639,6 @@
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 }
 
-/**
- * Expect fallback to internal viewport if device is external and external viewport is not present.
- */
-TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) {
-    prepareAxes(POSITION);
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    mDevice->setExternal(true);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
-
-    NotifyMotionArgs motionArgs;
-
-    // Expect the event to be sent to the internal viewport,
-    // because an external viewport is not present.
-    processPosition(mapper, 100, 100);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
-
-    // Expect the event to be sent to the external viewport if it is present.
-    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
-    processPosition(mapper, 100, 100);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
-}
-
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
     // Setup for second display.
     sp<FakePointerController> fakePointerController = new FakePointerController();
@@ -6516,27 +6675,28 @@
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
     constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
     InputDeviceIdentifier identifier;
     identifier.name = "TOUCHSCREEN2";
     identifier.location = USB2;
     std::unique_ptr<InputDevice> device2 =
             std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-                                          DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
-    mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->setAbsoluteAxisValue(SECOND_DEVICE_ID, ABS_MT_SLOT, 0 /*value*/);
-    mFakeEventHub->addConfigurationProperty(SECOND_DEVICE_ID, String8("touch.deviceType"),
-            String8("touchScreen"));
+                                          identifier);
+    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->setAbsoluteAxisValue(SECOND_EVENTHUB_ID, ABS_MT_SLOT, 0 /*value*/);
+    mFakeEventHub->addConfigurationProperty(SECOND_EVENTHUB_ID, String8("touch.deviceType"),
+                                            String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>();
+    MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID);
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
 
@@ -6597,7 +6757,7 @@
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     std::vector<TouchVideoFrame> frames{frame};
-    mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+    mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
     processPosition(mapper, 100, 200);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6627,7 +6787,7 @@
         clearViewports();
         prepareDisplay(orientation);
         std::vector<TouchVideoFrame> frames{frame};
-        mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+        mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
         processPosition(mapper, 100, 200);
         processSync(mapper);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6649,7 +6809,7 @@
     NotifyMotionArgs motionArgs;
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+    mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
     processPosition(mapper, 100, 200);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6823,4 +6983,41 @@
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 }
 
+// --- MultiTouchInputMapperTest_ExternalDevice ---
+
+class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
+protected:
+    virtual void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
+    }
+};
+
+/**
+ * Expect fallback to internal viewport if device is external and external viewport is not present.
+ */
+TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+    NotifyMotionArgs motionArgs;
+
+    // Expect the event to be sent to the internal viewport,
+    // because an external viewport is not present.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
+
+    // Expect the event to be sent to the external viewport if it is present.
+    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
 } // namespace android
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 7b3af70..3e0f136 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -1,11 +1,25 @@
 cc_library_shared {
     name: "libpowermanager",
 
-    srcs: ["IPowerManager.cpp"],
+    srcs: [
+        "IPowerManager.cpp",
+        "Temperature.cpp",
+        "CoolingDevice.cpp",
+        ":libpowermanager_aidl",
+    ],
+
+    aidl: {
+       local_include_dirs: ["."],
+       include_dirs: [
+           "frameworks/base/core/java/android/os",
+       ],
+       export_aidl_headers: true
+    },
 
     shared_libs: [
         "libutils",
         "libbinder",
+        "liblog"
     ],
 
     cflags: [
@@ -15,3 +29,22 @@
         "-Wunreachable-code",
     ],
 }
+
+cc_test {
+    name: "thermalmanager-test",
+    srcs: ["IThermalManagerTest.cpp",
+          ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libpowermanager",
+        "libbinder",
+        "libutils",
+    ],
+}
diff --git a/services/powermanager/CoolingDevice.cpp b/services/powermanager/CoolingDevice.cpp
new file mode 100644
index 0000000..ebbc1b4
--- /dev/null
+++ b/services/powermanager/CoolingDevice.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "CoolingDevice"
+
+#include <android/CoolingDevice.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace os {
+
+status_t CoolingDevice::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->readFloat(&mValue);
+    parcel->readUint32(&mType);
+    parcel->readString16(&mName);
+
+    return OK;
+}
+
+status_t CoolingDevice::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->writeFloat(mValue);
+    parcel->writeUint32(mType);
+    parcel->writeString16(mName);
+
+    return OK;
+}
+
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/IThermalManagerTest.cpp
new file mode 100644
index 0000000..575b9ee
--- /dev/null
+++ b/services/powermanager/IThermalManagerTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 "ThermalManagerTest"
+//#define LOG_NDEBUG 0
+
+#include <thread>
+
+#include <android/os/BnThermalStatusListener.h>
+#include <android/os/IThermalService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <condition_variable>
+#include <gtest/gtest.h>
+#include <powermanager/PowerManager.h>
+#include <utils/Log.h>
+
+using namespace android;
+using namespace android::os;
+using namespace std::chrono_literals;
+
+class IThermalServiceTest : public testing::Test,
+                            public BnThermalStatusListener{
+    public:
+        IThermalServiceTest();
+        void setThermalOverride(int level);
+        virtual binder::Status onStatusChange(int status) override;
+        int getStatusFromService();
+        void SetUp() override;
+        void TearDown() override;
+    protected:
+        sp<IThermalService> mThermalSvc;
+        std::condition_variable mCondition;
+        int mListenerStatus;
+        int mServiceStatus;
+        std::mutex mMutex;
+};
+
+IThermalServiceTest::IThermalServiceTest()
+ : mListenerStatus(0),
+   mServiceStatus(0) {
+}
+
+void IThermalServiceTest::setThermalOverride(int level) {
+    std::string cmdStr = "cmd thermalservice override-status " + std::to_string(level);
+    system(cmdStr.c_str());
+}
+
+binder::Status IThermalServiceTest::onStatusChange(int status) {
+    std::unique_lock<std::mutex> lock(mMutex);
+    mListenerStatus = status;
+    ALOGI("IThermalServiceTest::notifyListener %d", mListenerStatus);
+    mCondition.notify_all();
+    return binder::Status::ok();
+}
+
+int IThermalServiceTest::getStatusFromService() {
+    int status;
+    binder::Status ret = mThermalSvc->getCurrentThermalStatus(&status);
+    if (ret.isOk()) {
+        return status;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+void IThermalServiceTest::SetUp() {
+    setThermalOverride(0);
+    // use checkService() to avoid blocking if thermal service is not up yet
+    sp<IBinder> binder =
+        defaultServiceManager()->checkService(String16("thermalservice"));
+    EXPECT_NE(binder, nullptr);
+    mThermalSvc = interface_cast<IThermalService>(binder);
+    EXPECT_NE(mThermalSvc, nullptr);
+    bool success = false;
+    binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success);
+    ASSERT_TRUE(success);
+    ASSERT_TRUE(ret.isOk());
+    // Wait for listener called after registration, shouldn't timeout
+    std::unique_lock<std::mutex> lock(mMutex);
+    EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
+}
+
+void IThermalServiceTest::TearDown() {
+    bool success = false;
+    binder::Status ret = mThermalSvc->unregisterThermalStatusListener(this, &success);
+    ASSERT_TRUE(success);
+    ASSERT_TRUE(ret.isOk());
+}
+
+class IThermalListenerTest : public IThermalServiceTest, public testing::WithParamInterface<int32_t> {
+  public:
+    static auto PrintParam(const testing::TestParamInfo<ParamType> &info) {
+        return std::to_string(info.param);
+    }
+};
+
+TEST_P(IThermalListenerTest, TestListener) {
+    int level = GetParam();
+    std::unique_lock<std::mutex> lock(mMutex);
+    // Set the override thermal status
+    setThermalOverride(level);
+    // Wait for listener called, shouldn't timeout
+    EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
+    // Check the result
+    EXPECT_EQ(level, mListenerStatus);
+    ALOGI("Thermal listener status %d, expecting %d", mListenerStatus, level);
+}
+
+INSTANTIATE_TEST_SUITE_P(TestListenerLevels, IThermalListenerTest, testing::Range(
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_LIGHT),
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_SHUTDOWN)),
+        IThermalListenerTest::PrintParam);
+
+class IThermalLevelTest : public IThermalServiceTest, public testing::WithParamInterface<int32_t> {
+  public:
+    static auto PrintParam(const testing::TestParamInfo<ParamType> &info) {
+        return std::to_string(info.param);
+    }
+};
+
+TEST_P(IThermalLevelTest, TestGetStatusLevel) {
+    int level = GetParam();
+    setThermalOverride(level);
+    mServiceStatus = getStatusFromService();
+    EXPECT_EQ(level, mServiceStatus);
+}
+
+INSTANTIATE_TEST_SUITE_P(TestStatusLevels, IThermalLevelTest, testing::Range(
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_NONE),
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_SHUTDOWN)),
+        IThermalLevelTest::PrintParam);
+
+int main(int argc, char **argv) {
+    std::unique_ptr<std::thread> binderLoop;
+    binderLoop = std::make_unique<std::thread>(
+            [&] { IPCThreadState::self()->joinThreadPool(true); });
+
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGV("Test result = %d\n", status);
+
+    return status;
+}
\ No newline at end of file
diff --git a/services/powermanager/Temperature.cpp b/services/powermanager/Temperature.cpp
new file mode 100644
index 0000000..8ec0a87
--- /dev/null
+++ b/services/powermanager/Temperature.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "Temperature"
+
+#include <android/Temperature.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace os {
+
+status_t Temperature::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->readFloat(&mValue);
+    parcel->readInt32(&mType);
+    parcel->readString16(&mName);
+    parcel->readInt32(&mStatus);
+
+    return OK;
+}
+
+status_t Temperature::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->writeFloat(mValue);
+    parcel->writeInt32(mType);
+    parcel->writeString16(mName);
+    parcel->writeInt32(mStatus);
+
+    return OK;
+}
+
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 4a0d6ee..d3712d9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -57,6 +57,9 @@
     // Forces a color mode on the outputs being refreshed
     ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
 
+    // If true, GPU clocks will be increased when rendering blurs
+    bool blursAreExpensive{false};
+
     // If true, the complete output geometry needs to be recomputed this frame
     bool updatingOutputGeometryThisFrame{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 9622e78..a5711a3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -265,7 +265,8 @@
     virtual void prepareFrame() = 0;
     virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
     virtual void finishFrame(const CompositionRefreshArgs&) = 0;
-    virtual std::optional<base::unique_fd> composeSurfaces(const Region&) = 0;
+    virtual std::optional<base::unique_fd> composeSurfaces(
+            const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
     virtual void postFramebuffer() = 0;
     virtual void chooseCompositionStrategy() = 0;
     virtual bool getSkipColorTransform() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index d41337c..6f25e63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -83,7 +83,8 @@
     void prepareFrame() override;
     void devOptRepaintFlash(const CompositionRefreshArgs&) override;
     void finishFrame(const CompositionRefreshArgs&) override;
-    std::optional<base::unique_fd> composeSurfaces(const Region&) override;
+    std::optional<base::unique_fd> composeSurfaces(
+            const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
     void postFramebuffer() override;
     void cacheClientCompositionRequests(uint32_t) override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 346c2d1..4661c5d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -98,7 +98,10 @@
 
     MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
 
-    MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+    MOCK_METHOD2(composeSurfaces,
+                 std::optional<base::unique_fd>(
+                         const Region&,
+                         const compositionengine::CompositionRefreshArgs& refreshArgs));
     MOCK_CONST_METHOD0(getSkipColorTransform, bool());
 
     MOCK_METHOD0(postFramebuffer, void());
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e792f45..248933e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -774,7 +774,7 @@
         if (!dirtyRegion.isEmpty()) {
             base::unique_fd readyFence;
             // redraw the whole screen
-            static_cast<void>(composeSurfaces(dirtyRegion));
+            static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
 
             mRenderSurface->queueBuffer(std::move(readyFence));
         }
@@ -787,7 +787,7 @@
     prepareFrame();
 }
 
-void Output::finishFrame(const compositionengine::CompositionRefreshArgs&) {
+void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
@@ -797,7 +797,7 @@
 
     // Repaint the framebuffer (if needed), getting the optional fence for when
     // the composition completes.
-    auto optReadyFence = composeSurfaces(Region::INVALID_REGION);
+    auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs);
     if (!optReadyFence) {
         return;
     }
@@ -806,7 +806,8 @@
     mRenderSurface->queueBuffer(std::move(*optReadyFence));
 }
 
-std::optional<base::unique_fd> Output::composeSurfaces(const Region& debugRegion) {
+std::optional<base::unique_fd> Output::composeSurfaces(
+        const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
@@ -894,8 +895,10 @@
     // or complex GPU shaders and it's expensive. We boost the GPU frequency so that
     // GPU composition can finish in time. We must reset GPU frequency afterwards,
     // because high frequency consumes extra battery.
+    const bool expensiveBlurs =
+            refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
     const bool expensiveRenderingExpected =
-            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
+            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 || expensiveBlurs;
     if (expensiveRenderingExpected) {
         setExpensiveRenderingExpected(true);
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index be0e9e4..63bb459 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2463,7 +2463,9 @@
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
         MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
-        MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+        MOCK_METHOD2(composeSurfaces,
+                     std::optional<base::unique_fd>(
+                             const Region&, const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD0(postFramebuffer, void());
         MOCK_METHOD0(prepareFrame, void());
     };
@@ -2526,7 +2528,7 @@
 
     InSequence seq;
     EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion)));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, prepareFrame());
@@ -2542,7 +2544,9 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+        MOCK_METHOD2(composeSurfaces,
+                     std::optional<base::unique_fd>(
+                             const Region&, const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD0(postFramebuffer, void());
     };
 
@@ -2568,7 +2572,7 @@
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _));
 
     mOutput.finishFrame(mRefreshArgs);
 }
@@ -2577,7 +2581,7 @@
     mOutput.mState.isEnabled = true;
 
     InSequence seq;
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)))
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _))
             .WillOnce(Return(ByMove(base::unique_fd())));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
 
@@ -2804,7 +2808,8 @@
 
     struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
         auto execute() {
-            getInstance()->mReadyFence = getInstance()->mOutput.composeSurfaces(kDebugRegion);
+            getInstance()->mReadyFence =
+                    getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
             return nextState<FenceCheckState>();
         }
     };
@@ -2832,6 +2837,7 @@
     static const mat4 kDefaultColorTransformMat;
 
     static const Region kDebugRegion;
+    static const compositionengine::CompositionRefreshArgs kDefaultRefreshArgs;
     static const HdrCapabilities kHdrCapabilities;
 
     StrictMock<mock::CompositionEngine> mCompositionEngine;
@@ -2851,6 +2857,7 @@
 const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
 const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
 const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
+const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
 const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
 const HdrCapabilities OutputComposeSurfacesTest::
         kHdrCapabilities{{},
@@ -3193,7 +3200,7 @@
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
@@ -3201,7 +3208,7 @@
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
@@ -3213,7 +3220,7 @@
     EXPECT_CALL(mRenderEngine, useProtectedContext(false));
     EXPECT_CALL(*mRenderSurface, setProtected(false));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -3233,7 +3240,7 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
@@ -3243,7 +3250,7 @@
     EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
@@ -3254,7 +3261,7 @@
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
     EXPECT_CALL(mRenderEngine, useProtectedContext(true));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
@@ -3265,7 +3272,7 @@
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
     EXPECT_CALL(*mRenderSurface, setProtected(true));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
@@ -3276,7 +3283,7 @@
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
     EXPECT_CALL(mRenderEngine, useProtectedContext(true));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
 
 struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -3302,7 +3309,43 @@
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
 
-    mOutput.composeSurfaces(kDebugRegion);
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
+      : public OutputComposeSurfacesTest_SetsExpensiveRendering {
+    OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
+        mLayer.layerFEState.backgroundBlurRadius = 10;
+        mOutput.editState().isEnabled = true;
+
+        EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true));
+        EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+                .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer.outputLayer));
+    }
+
+    NonInjectedLayer mLayer;
+    compositionengine::CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
+    mRefreshArgs.blursAreExpensive = true;
+    mOutput.updateAndWriteCompositionState(mRefreshArgs);
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
+    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
+    mRefreshArgs.blursAreExpensive = false;
+    mOutput.updateAndWriteCompositionState(mRefreshArgs);
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
+    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
 }
 
 /*
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index 9d45e33..44d4d75 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -69,6 +69,8 @@
         // Set color for color fill settings.
         layerSettings->source.solidColor = getColor().rgb;
         results.push_back(*layerSettings);
+    } else if (hasBlur()) {
+        results.push_back(*layerSettings);
     }
 
     return results;
@@ -148,6 +150,10 @@
             mDrawingState.color.b >= 0.0_hf;
 }
 
+bool EffectLayer::hasBlur() const {
+    return getBackgroundBlurRadius() > 0;
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h
index 33758b6..1dcb633 100644
--- a/services/surfaceflinger/EffectLayer.h
+++ b/services/surfaceflinger/EffectLayer.h
@@ -62,7 +62,9 @@
 private:
     // Returns true if there is a valid color to fill.
     bool fillsColor() const;
-    bool hasSomethingToDraw() const { return fillsColor() || drawShadows(); }
+    // Returns true if this layer has a blur value.
+    bool hasBlur() const;
+    bool hasSomethingToDraw() const { return fillsColor() || drawShadows() || hasBlur(); }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2acf084..ce0a8a1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -363,11 +363,13 @@
 
     property_get("ro.surface_flinger.supports_background_blur", value, "0");
     bool supportsBlurs = atoi(value);
-    property_get("debug.sf.disableBlurs", value, "0");
+    property_get("debug.sf.disable_blurs", value, "0");
     bool disableBlurs = atoi(value);
     mEnableBlurs = supportsBlurs && !disableBlurs;
     ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs,
              disableBlurs);
+    property_get("ro.sf.blurs_are_expensive", value, "0");
+    mBlursAreExpensive = atoi(value);
 
     const size_t defaultListSize = MAX_LAYERS;
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
@@ -1926,6 +1928,7 @@
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
     refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
+    refreshArgs.blursAreExpensive = mBlursAreExpensive;
 
     if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
         refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index dcbb150..c79621b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1043,7 +1043,10 @@
     const std::shared_ptr<TimeStats> mTimeStats;
     const std::unique_ptr<FrameTracer> mFrameTracer;
     bool mUseHwcVirtualDisplays = false;
+    // Disable blurs, for debugging
     bool mEnableBlurs = false;
+    // If blurs are considered expensive and should require high GPU frequency.
+    bool mBlursAreExpensive = false;
     std::atomic<uint32_t> mFrameMissedCount = 0;
     std::atomic<uint32_t> mHwcFrameMissedCount = 0;
     std::atomic<uint32_t> mGpuFrameMissedCount = 0;