Merge "Change output format to not throw warning on volantis." into lmp-dev
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index ba8edf2..bd721c8 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -570,7 +570,6 @@
     return 0;
 }
 
-
 int create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set)
 {
     char *tmp;
@@ -633,6 +632,46 @@
     ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));
 }
 
+static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name,
+    const char* output_file_name, const char *pkgname, const char *instruction_set)
+{
+    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
+    static const unsigned int MAX_INSTRUCTION_SET_LEN = 32;
+
+    static const char* PATCHOAT_BIN = "/system/bin/patchoat";
+    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
+        ALOGE("Instruction set %s longer than max length of %d",
+              instruction_set, MAX_INSTRUCTION_SET_LEN);
+        return;
+    }
+
+    /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/
+    char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
+    char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN];
+    char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN];
+    const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art";
+    // The caller has already gotten all the locks we need.
+    const char* no_lock_arg = "--no-lock-output";
+    sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
+    sprintf(output_oat_fd_arg, "--output-oat-fd=%d", oat_fd);
+    sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_fd);
+    ALOGE("Running %s isa=%s in-fd=%d (%s) out-fd=%d (%s)\n",
+          PATCHOAT_BIN, instruction_set, input_fd, input_file_name, oat_fd, output_file_name);
+
+    /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */
+    char* argv[7];
+    argv[0] = (char*) PATCHOAT_BIN;
+    argv[1] = (char*) patched_image_location_arg;
+    argv[2] = (char*) no_lock_arg;
+    argv[3] = instruction_set_arg;
+    argv[4] = output_oat_fd_arg;
+    argv[5] = input_oat_fd_arg;
+    argv[6] = NULL;
+
+    execv(PATCHOAT_BIN, (char* const *)argv);
+    ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
+}
+
 static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
     const char* output_file_name, const char *pkgname, const char *instruction_set)
 {
@@ -645,10 +684,21 @@
     char dex2oat_Xmx_flag[PROPERTY_VALUE_MAX];
     bool have_dex2oat_Xmx_flag = property_get("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
 
+    char dex2oat_compiler_filter_flag[PROPERTY_VALUE_MAX];
+    bool have_dex2oat_compiler_filter_flag = property_get("dalvik.vm.dex2oat-filter",
+                                                          dex2oat_compiler_filter_flag, NULL) > 0;
+
     char dex2oat_flags[PROPERTY_VALUE_MAX];
     bool have_dex2oat_flags = property_get("dalvik.vm.dex2oat-flags", dex2oat_flags, NULL) > 0;
     ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
 
+    // If we booting without the real /data, don't spend time compiling.
+    char vold_decrypt[PROPERTY_VALUE_MAX];
+    bool have_vold_decrypt = property_get("vold.decrypt", vold_decrypt, "") > 0;
+    bool skip_compilation = (have_vold_decrypt &&
+                             (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
+                             (strcmp(vold_decrypt, "1") == 0)));
+
     static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
 
     static const char* RUNTIME_ARG = "--runtime-arg";
@@ -671,6 +721,7 @@
     char top_k_profile_threshold_arg[strlen("--top-k-profile-threshold=") + PROPERTY_VALUE_MAX];
     char dex2oat_Xms_arg[strlen("-Xms") + PROPERTY_VALUE_MAX];
     char dex2oat_Xmx_arg[strlen("-Xmx") + PROPERTY_VALUE_MAX];
+    char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + PROPERTY_VALUE_MAX];
 
     sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
     sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
@@ -702,6 +753,12 @@
     if (have_dex2oat_Xmx_flag) {
         sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
     }
+    if (skip_compilation) {
+        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
+        have_dex2oat_compiler_filter_flag = true;
+    } else if (have_dex2oat_compiler_filter_flag) {
+        sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag);
+    }
 
     ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
 
@@ -710,6 +767,7 @@
                + (have_top_k_profile_threshold ? 1 : 0)
                + (have_dex2oat_Xms_flag ? 2 : 0)
                + (have_dex2oat_Xmx_flag ? 2 : 0)
+               + (have_dex2oat_compiler_filter_flag ? 1 : 0)
                + (have_dex2oat_flags ? 1 : 0)];
     int i = 0;
     argv[i++] = (char*)DEX2OAT_BIN;
@@ -732,6 +790,9 @@
         argv[i++] = (char*)RUNTIME_ARG;
         argv[i++] = dex2oat_Xmx_arg;
     }
+    if (have_dex2oat_compiler_filter_flag) {
+        argv[i++] = dex2oat_compiler_filter_arg;
+    }
     if (have_dex2oat_flags) {
         argv[i++] = dex2oat_flags;
     }
@@ -769,14 +830,17 @@
 }
 
 int dexopt(const char *apk_path, uid_t uid, int is_public,
-           const char *pkgname, const char *instruction_set)
+           const char *pkgname, const char *instruction_set,
+           int is_patchoat)
 {
     struct utimbuf ut;
-    struct stat apk_stat, dex_stat;
+    struct stat input_stat, dex_stat;
     char out_path[PKG_PATH_MAX];
     char persist_sys_dalvik_vm_lib[PROPERTY_VALUE_MAX];
     char *end;
-    int res, zip_fd=-1, out_fd=-1;
+    const char *input_file;
+    char in_odex_path[PKG_PATH_MAX];
+    int res, input_fd=-1, out_fd=-1;
 
     if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
         return -1;
@@ -785,12 +849,20 @@
     /* The command to run depend on the value of persist.sys.dalvik.vm.lib */
     property_get("persist.sys.dalvik.vm.lib.2", persist_sys_dalvik_vm_lib, "libart.so");
 
+    if (is_patchoat && strncmp(persist_sys_dalvik_vm_lib, "libart", 6) != 0) {
+        /* We may only patch if we are libart */
+        ALOGE("Patching is only supported in libart\n");
+        return -1;
+    }
+
     /* Before anything else: is there a .odex file?  If so, we have
      * precompiled the apk and there is nothing to do here.
+     *
+     * We skip this if we are doing a patchoat.
      */
     strcpy(out_path, apk_path);
     end = strrchr(out_path, '.');
-    if (end != NULL) {
+    if (end != NULL && !is_patchoat) {
         strcpy(end, ".odex");
         if (stat(out_path, &dex_stat) == 0) {
             return 0;
@@ -801,12 +873,33 @@
         return -1;
     }
 
-    memset(&apk_stat, 0, sizeof(apk_stat));
-    stat(apk_path, &apk_stat);
+    if (is_patchoat) {
+        /* /system/framework/whatever.jar -> /system/framework/<isa>/whatever.odex */
+        strcpy(in_odex_path, apk_path);
+        end = strrchr(in_odex_path, '/');
+        if (end == NULL) {
+            ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
+            return -1;
+        }
+        const char *apk_end = apk_path + (end - in_odex_path); // strrchr(apk_path, '/');
+        strcpy(end + 1, instruction_set); // in_odex_path now is /system/framework/<isa>\0
+        strcat(in_odex_path, apk_end);
+        end = strrchr(in_odex_path, '.');
+        if (end == NULL) {
+            return -1;
+        }
+        strcpy(end + 1, "odex");
+        input_file = in_odex_path;
+    } else {
+        input_file = apk_path;
+    }
 
-    zip_fd = open(apk_path, O_RDONLY, 0);
-    if (zip_fd < 0) {
-        ALOGE("installd cannot open '%s' for input during dexopt\n", apk_path);
+    memset(&input_stat, 0, sizeof(input_stat));
+    stat(input_file, &input_stat);
+
+    input_fd = open(input_file, O_RDONLY, 0);
+    if (input_fd < 0) {
+        ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
         return -1;
     }
 
@@ -833,7 +926,7 @@
     }
 
 
-    ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
+    ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
 
     pid_t pid;
     pid = fork();
@@ -863,9 +956,13 @@
         }
 
         if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
-            run_dexopt(zip_fd, out_fd, apk_path, out_path);
+            run_dexopt(input_fd, out_fd, input_file, out_path);
         } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
-            run_dex2oat(zip_fd, out_fd, apk_path, out_path, pkgname, instruction_set);
+            if (is_patchoat) {
+                run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
+            } else {
+                run_dex2oat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
+            }
         } else {
             exit(69);   /* Unexpected persist.sys.dalvik.vm.lib value */
         }
@@ -873,19 +970,19 @@
     } else {
         res = wait_child(pid);
         if (res == 0) {
-            ALOGV("DexInv: --- END '%s' (success) ---\n", apk_path);
+            ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
         } else {
-            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", apk_path, res);
+            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
             goto fail;
         }
     }
 
-    ut.actime = apk_stat.st_atime;
-    ut.modtime = apk_stat.st_mtime;
+    ut.actime = input_stat.st_atime;
+    ut.modtime = input_stat.st_mtime;
     utime(out_path, &ut);
 
     close(out_fd);
-    close(zip_fd);
+    close(input_fd);
     return 0;
 
 fail:
@@ -893,8 +990,8 @@
         close(out_fd);
         unlink(out_path);
     }
-    if (zip_fd >= 0) {
-        close(zip_fd);
+    if (input_fd >= 0) {
+        close(input_fd);
     }
     return -1;
 }
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 62579b9..746ce57 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -39,7 +39,7 @@
 static int do_dexopt(char **arg, char reply[REPLY_MAX])
 {
         /* apk_path, uid, is_public, pkgname, instruction_set */
-    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4]);
+    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 0);
 }
 
 static int do_move_dex(char **arg, char reply[REPLY_MAX])
@@ -151,6 +151,10 @@
     return prune_dex_cache(arg[0] /* subdirectory name */);
 }
 
+static int do_patchoat(char **arg, char reply[REPLY_MAX]) {
+    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 1);
+}
+
 struct cmdinfo {
     const char *name;
     unsigned numargs;
@@ -179,6 +183,7 @@
     { "idmap",                3, do_idmap },
     { "restorecondata",       3, do_restorecon_data },
     { "prunedexcache",        1, do_prune_dex_cache },
+    { "patchoat",             5, do_patchoat },
 };
 
 static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 7a5da98..9d420bd 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -217,7 +217,7 @@
              int64_t *codesize, int64_t *datasize, int64_t *cachesize, int64_t *asecsize);
 int free_cache(int64_t free_size);
 int dexopt(const char *apk_path, uid_t uid, int is_public, const char *pkgName,
-           const char *instruction_set);
+           const char *instruction_set, int should_relocate);
 int movefiles();
 int linklib(const char* target, const char* source, int userId);
 int idmap(const char *target_path, const char *overlay_path, uid_t uid);
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 77a5a1c..d58c460 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -210,11 +210,18 @@
 
 /*
  * Returns the default sensor for the given type, or NULL if no sensor
- * of that type exist.
+ * of that type exists.
  */
 ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type);
 
 /*
+ * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor
+ * of this type and wakeUp properties exists.
+ */
+ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type,
+        bool wakeUp);
+
+/*
  * Creates a new sensor event queue and associate it with a looper.
  */
 ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
@@ -321,6 +328,11 @@
  */
 int ASensor_getReportingMode(ASensor const* sensor);
 
+/*
+ * Returns true if this is a wake up sensor, false otherwise.
+ */
+bool ASensor_isWakeUpSensor(ASensor const* sensor);
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/media/openmax/OMX_Audio.h b/include/media/openmax/OMX_Audio.h
index 89ce0fc..f4cb643 100644
--- a/include/media/openmax/OMX_Audio.h
+++ b/include/media/openmax/OMX_Audio.h
@@ -278,6 +278,7 @@
 #define OMX_AUDIO_AACToolTNS  0x00000004 /**< TNS: Temporal Noise Shaping tool allowed or active */
 #define OMX_AUDIO_AACToolPNS  0x00000008 /**< PNS: MPEG-4 Perceptual Noise substitution tool allowed or active */
 #define OMX_AUDIO_AACToolLTP  0x00000010 /**< LTP: MPEG-4 Long Term Prediction tool allowed or active */
+#define OMX_AUDIO_AACToolVendor 0x00010000 /**< NOT A KHRONOS VALUE, offset for vendor-specific additions */
 #define OMX_AUDIO_AACToolAll  0x7FFFFFFF /**< all AAC tools allowed or active (*/
 
 /** MPEG-4 AAC error resilience (ER) tool usage (for nAACERtools in OMX_AUDIO_PARAM_AACPROFILETYPE).
diff --git a/include/media/openmax/OMX_AudioExt.h b/include/media/openmax/OMX_AudioExt.h
index dc6457b..8a2c4de 100644
--- a/include/media/openmax/OMX_AudioExt.h
+++ b/include/media/openmax/OMX_AudioExt.h
@@ -40,6 +40,9 @@
  */
 #include <OMX_Core.h>
 
+#define OMX_AUDIO_AACToolAndroidSSBR (OMX_AUDIO_AACToolVendor << 0) /**< SSBR: MPEG-4 Single-rate (downsampled) Spectral Band Replication tool allowed or active */
+#define OMX_AUDIO_AACToolAndroidDSBR (OMX_AUDIO_AACToolVendor << 1) /**< DSBR: MPEG-4 Dual-rate Spectral Band Replication tool allowed or active */
+
 typedef enum OMX_AUDIO_CODINGEXTTYPE {
     OMX_AUDIO_CodingAndroidUnused = OMX_AUDIO_CodingKhronosExtensions + 0x00100000,
     OMX_AUDIO_CodingAndroidAC3,         /**< AC3 encoded data */
@@ -69,6 +72,17 @@
                                    limit the audio signal. Use 0 to let encoder decide */
 } OMX_AUDIO_PARAM_ANDROID_OPUSTYPE;
 
+typedef struct OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE {
+    OMX_U32 nSize;            /**< size of the structure in bytes */
+    OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
+    OMX_S32 nMaxOutputChannels;    /**< Maximum channel count to be output, -1 if unspecified, 0 if downmixing disabled */
+    OMX_S32 nDrcCut;               /**< The DRC attenuation factor, between 0 and 127, -1 if unspecified */
+    OMX_S32 nDrcBoost;             /**< The DRC amplification factor, between 0 and 127, -1 if unspecified */
+    OMX_S32 nHeavyCompression;     /**< 0 for light compression, 1 for heavy compression, -1 if unspecified */
+    OMX_S32 nTargetReferenceLevel; /**< Target reference level, between 0 and 127, -1 if unspecified */
+    OMX_S32 nEncodedTargetLevel;   /**< Target reference level assumed at the encoder, between 0 and 127, -1 if unspecified */
+} OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index 7070809..6cd774b 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -59,6 +59,7 @@
     OMX_IndexExtAudioStartUnused = OMX_IndexKhronosExtensions + 0x00400000,
     OMX_IndexParamAudioAndroidAc3,                  /**< reference: OMX_AUDIO_PARAM_ANDROID_AC3TYPE */
     OMX_IndexParamAudioAndroidOpus,                 /**< reference: OMX_AUDIO_PARAM_ANDROID_OPUSTYPE */
+    OMX_IndexParamAudioAndroidAacPresentation,      /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */
 
     /* Image parameters and configurations */
     OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
index b80da56..7b4fa2f 100644
--- a/libs/gui/SensorManager.cpp
+++ b/libs/gui/SensorManager.cpp
@@ -116,12 +116,23 @@
 {
     Mutex::Autolock _l(mLock);
     if (assertStateLocked() == NO_ERROR) {
+        bool wakeUpSensor = false;
+        // For the following sensor types, return a wake-up sensor. These types are by default
+        // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return
+        // a non_wake-up version.
+        if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
+            type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
+            type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE) {
+            wakeUpSensor = true;
+        }
         // For now we just return the first sensor of that type we find.
         // in the future it will make sense to let the SensorService make
         // that decision.
         for (size_t i=0 ; i<mSensors.size() ; i++) {
-            if (mSensorList[i]->getType() == type)
+            if (mSensorList[i]->getType() == type &&
+                mSensorList[i]->isWakeUpSensor() == wakeUpSensor) {
                 return mSensorList[i];
+            }
         }
     }
     return NULL;
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index a750a3d..ce14f99 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -2366,9 +2366,38 @@
 
     policyFlags |= POLICY_FLAG_TRUSTED;
 
+    int32_t keyCode = args->keyCode;
+    if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
+        int32_t newKeyCode = AKEYCODE_UNKNOWN;
+        if (keyCode == AKEYCODE_DEL) {
+            newKeyCode = AKEYCODE_BACK;
+        } else if (keyCode == AKEYCODE_ENTER) {
+            newKeyCode = AKEYCODE_HOME;
+        }
+        if (newKeyCode != AKEYCODE_UNKNOWN) {
+            AutoMutex _l(mLock);
+            struct KeyReplacement replacement = {keyCode, args->deviceId};
+            mReplacedKeys.add(replacement, newKeyCode);
+            keyCode = newKeyCode;
+            metaState &= ~AMETA_META_ON;
+        }
+    } else if (args->action == AKEY_EVENT_ACTION_UP) {
+        // In order to maintain a consistent stream of up and down events, check to see if the key
+        // going up is one we've replaced in a down event and haven't yet replaced in an up event,
+        // even if the modifier was released between the down and the up events.
+        AutoMutex _l(mLock);
+        struct KeyReplacement replacement = {keyCode, args->deviceId};
+        ssize_t index = mReplacedKeys.indexOfKey(replacement);
+        if (index >= 0) {
+            keyCode = mReplacedKeys.valueAt(index);
+            mReplacedKeys.removeItemsAt(index);
+            metaState &= ~AMETA_META_ON;
+        }
+    }
+
     KeyEvent event;
     event.initialize(args->deviceId, args->source, args->action,
-            flags, args->keyCode, args->scanCode, metaState, 0,
+            flags, keyCode, args->scanCode, metaState, 0,
             args->downTime, args->eventTime);
 
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -2391,7 +2420,7 @@
         int32_t repeatCount = 0;
         KeyEntry* newEntry = new KeyEntry(args->eventTime,
                 args->deviceId, args->source, policyFlags,
-                args->action, flags, args->keyCode, args->scanCode,
+                args->action, flags, keyCode, args->scanCode,
                 metaState, repeatCount, args->downTime);
 
         needWake = enqueueInboundEventLocked(newEntry);
@@ -3050,6 +3079,7 @@
 
     mTouchStatesByDisplay.clear();
     mLastHoverWindowHandle.clear();
+    mReplacedKeys.clear();
 }
 
 void InputDispatcher::logDispatchStateLocked() {
@@ -3188,6 +3218,18 @@
         dump.append(INDENT "InboundQueue: <empty>\n");
     }
 
+    if (!mReplacedKeys.isEmpty()) {
+        dump.append(INDENT "ReplacedKeys:\n");
+        for (size_t i = 0; i < mReplacedKeys.size(); i++) {
+            const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
+            int32_t newKeyCode = mReplacedKeys.valueAt(i);
+            dump.appendFormat(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n",
+                    i, replacement.keyCode, replacement.deviceId, newKeyCode);
+        }
+    } else {
+        dump.append(INDENT "ReplacedKeys: <empty>\n");
+    }
+
     if (!mConnectionsByFd.isEmpty()) {
         dump.append(INDENT "Connections:\n");
         for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 9439124..0d15d7b 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -904,6 +904,20 @@
     void resetKeyRepeatLocked();
     KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime);
 
+    // Key replacement tracking
+    struct KeyReplacement {
+        int32_t keyCode;
+        int32_t deviceId;
+        bool operator==(const KeyReplacement& rhs) const {
+            return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
+        }
+        bool operator<(const KeyReplacement& rhs) const {
+            return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+        }
+    };
+    // Maps the key code replaced, device id tuple to the key code it was replaced with
+    KeyedVector<KeyReplacement, int32_t> mReplacedKeys;
+
     // Deferred command processing.
     bool haveCommandsLocked() const;
     bool runCommandsLockedInterruptible();