Implement Camera Id Remapping in CameraService

0. We reuse the CAMERA_INJECT_EXTERNAL_CAMERA permission to restrict access to the new Camera ID Remapping functionality.
1. Introduce a new binder method remapCameraIds(CameraIdRemapping).
2. The CameraIdRemapping parcelable is just a map of {packageName -> {id0 -> id1}} and is used between CameraManager <-> CameraService.
3. The methods in ICameraService that deal with Camera ID simply call resolveCameraId(id) to find the actual ID to use. Behind the scenes, we also use the UID to lookup the packageName associated with the client to find the associated camera ID.
4. Every time we update the config, we disconnect the clients that are used by the packages that we plan to replace. See the impl in remapCameraIds.
5. For CameraManager listeners for packages which have active ID remapping, we trigger all callbacks (torch, statusUpdates) for both the id0 as well as id1 - meaning, if we get onStatusChanged(id1), and package com.instagram.android has an active remapping of {id0->id1}, we’ll trigger onStatusChanged(id0) as well (to allow the app to receive updates on the camera it thinks it is talking to).
6. Because the CameraDevice in the application layer still only knows about the original (pre-configuration) IDs, the CaptureRequests it sends down also contain that original ID. So, we have to change some logic in CameraDeviceClient to allow it to modify the id while processing the requests. This also means that CameraDeviceClient should know both its remapped ID and the original Id it was created for.
7. To simulate and demonstrate live updates to the id mapping configuration, we add a shell command to invoke remapCameraIds dynamically via `adb shell cmd media.camera remap-camera-id packageName id0 id1`. This will only be allowed via adb root.

Security Bug: b/288901406 (For clearance related to the effort and the permission design)
Bug: b/286287541
Test: Manual testing

Change-Id: I785a70bffad304583cfe557114b003a51cf5ac43
Merged-In: Ie1d726c45032ee8e7b9773640f96465451167dc9
diff --git a/camera/Android.bp b/camera/Android.bp
index b3f70f4..a3fd7f9 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -144,6 +144,7 @@
     srcs: [
         "aidl/android/hardware/CameraExtensionSessionStats.aidl",
         "aidl/android/hardware/ICameraService.aidl",
+        "aidl/android/hardware/CameraIdRemapping.aidl",
         "aidl/android/hardware/ICameraServiceListener.aidl",
         "aidl/android/hardware/ICameraServiceProxy.aidl",
         "aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl",
diff --git a/camera/aidl/android/hardware/CameraIdRemapping.aidl b/camera/aidl/android/hardware/CameraIdRemapping.aidl
new file mode 100644
index 0000000..ab0a39c
--- /dev/null
+++ b/camera/aidl/android/hardware/CameraIdRemapping.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * Specifies a remapping of Camera Ids.
+ *
+ * Example: For a given package, a remapping of camera id0 to id1 specifies
+ * that any operation to perform on id0 should instead be performed on id1.
+ *
+ * @hide
+ */
+parcelable CameraIdRemapping {
+    /**
+     * Specifies remapping of Camera Ids per package.
+     */
+    parcelable PackageIdRemapping {
+        /** Package Name (e.g. com.android.xyz). */
+        String packageName;
+        /**
+         * Ordered list of Camera Ids to replace. Only Camera Ids present in this list will be
+         * affected.
+         */
+        String[] cameraIdToReplace;
+        /**
+         *  Ordered list of updated Camera Ids, where updatedCameraId[i] corresponds to
+         *  the updated camera id for cameraIdToReplace[i].
+         */
+        String[] updatedCameraId;
+    }
+
+    /**
+     * List of Camera Id remappings to perform.
+     */
+    List<PackageIdRemapping> packageIdRemapping;
+}
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index f8e1631..01b8ff8 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -29,6 +29,7 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.ICameraServiceListener;
 import android.hardware.CameraInfo;
+import android.hardware.CameraIdRemapping;
 import android.hardware.CameraStatus;
 import android.hardware.CameraExtensionSessionStats;
 
@@ -131,6 +132,22 @@
             int targetSdkVersion);
 
     /**
+     * Remap Camera Ids in the CameraService.
+     *
+     * Once this is in effect, all binder calls in the ICameraService that
+     * use logicalCameraId should consult remapping state to arrive at the
+     * correct cameraId to perform the operation on.
+     *
+     * Note: Before the new cameraIdRemapping state is applied, the previous
+     * state is cleared.
+     *
+     * @param cameraIdRemapping the camera ids to remap. Sending an unpopulated
+     *        cameraIdRemapping object will result in clearing of any previous
+     *        cameraIdRemapping state in the camera service.
+     */
+    void remapCameraIds(in CameraIdRemapping cameraIdRemapping);
+
+    /**
      * Remove listener for changes to camera device and flashlight state.
      */
     void removeListener(ICameraServiceListener listener);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index d1351cf..7551bd7 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -316,14 +316,24 @@
     for (auto& i : mListenerList) {
         if (shouldSkipStatusUpdates(systemCameraKind, i->isVendorListener(), i->getListenerPid(),
                 i->getListenerUid())) {
-            ALOGV("Skipping torch callback for system-only camera device %s",
-                    cameraId.c_str());
+            ALOGV("%s: Skipping torch callback for system-only camera device %s",
+                    __FUNCTION__, cameraId.c_str());
             continue;
         }
         auto ret = i->getListener()->onTorchStatusChanged(mapToInterface(status),
                 String16{cameraId});
         i->handleBinderStatus(ret, "%s: Failed to trigger onTorchStatusChanged for %d:%d: %d",
                 __FUNCTION__, i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
+        // Also trigger the torch callbacks for cameras that were remapped to the current cameraId
+        // for the specific package that this listener belongs to.
+        std::vector<String8> remappedCameraIds =
+                findOriginalIdsForRemappedCameraId(cameraId, i->getListenerUid());
+        for (auto& remappedCameraId : remappedCameraIds) {
+            ret = i->getListener()->onTorchStatusChanged(mapToInterface(status),
+                    String16(remappedCameraId));
+            i->handleBinderStatus(ret, "%s: Failed to trigger onTorchStatusChanged for %d:%d: %d",
+                    __FUNCTION__, i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
+        }
     }
 }
 
@@ -745,6 +755,144 @@
     return Status::ok();
 }
 
+Status CameraService::remapCameraIds(const hardware::CameraIdRemapping&
+      cameraIdRemapping) {
+    if (!checkCallingPermission(sCameraInjectExternalCameraPermission)) {
+        const int pid = CameraThreadState::getCallingPid();
+        const int uid = CameraThreadState::getCallingUid();
+        ALOGE("%s: Permission Denial: can't configure camera ID mapping pid=%d, uid=%d",
+                __FUNCTION__, pid, uid);
+        return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+                "Permission Denial: no permission to configure camera id mapping");
+    }
+    TCameraIdRemapping cameraIdRemappingMap{};
+    binder::Status parseStatus = parseCameraIdRemapping(cameraIdRemapping, cameraIdRemappingMap);
+    if (!parseStatus.isOk()) {
+        return parseStatus;
+    }
+    remapCameraIds(cameraIdRemappingMap);
+    return Status::ok();
+}
+
+Status CameraService::parseCameraIdRemapping(
+        const hardware::CameraIdRemapping& cameraIdRemapping,
+        TCameraIdRemapping cameraIdRemappingMap) {
+    String16 packageName;
+    String8 cameraIdToReplace, updatedCameraId;
+    for(const auto& packageIdRemapping: cameraIdRemapping.packageIdRemapping) {
+        packageName = packageIdRemapping.packageName;
+        if (packageName == String16("")) {
+            return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT,
+                    "CameraIdRemapping: Package name cannot be empty");
+        }
+        if (packageIdRemapping.cameraIdToReplace.size()
+            != packageIdRemapping.updatedCameraId.size()) {
+            return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
+                    "CameraIdRemapping: Mismatch in CameraId Remapping lists sizes for package %s",
+                     String8(packageName).c_str());
+        }
+        for(size_t i = 0; i < packageIdRemapping.cameraIdToReplace.size(); i++) {
+            cameraIdToReplace = String8(packageIdRemapping.cameraIdToReplace[i]);
+            updatedCameraId = String8(packageIdRemapping.updatedCameraId[i]);
+            cameraIdRemappingMap[packageName][cameraIdToReplace] = updatedCameraId;
+        }
+    }
+    return Status::ok();
+}
+
+void CameraService::remapCameraIds(const TCameraIdRemapping& cameraIdRemapping) {
+    // Acquire mServiceLock and prevent other clients from connecting
+    std::unique_ptr<AutoConditionLock> serviceLockWrapper =
+            AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
+
+    Mutex::Autolock lock(mCameraIdRemappingLock);
+    // This will disconnect all existing clients for camera Ids that are being
+    // remapped in cameraIdRemapping, but only if they were being used by an
+    // affected packageName.
+    std::vector<sp<BasicClient>> clientsToDisconnect;
+    std::vector<String8> cameraIdsToUpdate;
+    for (const auto& [packageName, injectionMap] : cameraIdRemapping) {
+        for (auto& [id0, id1] : injectionMap) {
+            ALOGI("%s: UPDATE:= %s: %s: %s", __FUNCTION__, String8(packageName).c_str(),
+                    id0.c_str(), id1.c_str());
+            auto clientDescriptor = mActiveClientManager.get(id0);
+            if (clientDescriptor != nullptr) {
+                sp<BasicClient> clientSp = clientDescriptor->getValue();
+                if (clientSp->getPackageName() == packageName) {
+                    // This camera ID is being used by the affected packageName.
+                    clientsToDisconnect.push_back(clientSp);
+                    cameraIdsToUpdate.push_back(id0);
+                }
+            }
+        }
+    }
+
+    // Update mCameraIdRemapping.
+    mCameraIdRemapping.clear();
+    mCameraIdRemapping.insert(cameraIdRemapping.begin(), cameraIdRemapping.end());
+
+    // Do not hold mServiceLock while disconnecting clients, but retain the condition
+    // blocking other clients from connecting in mServiceLockWrapper if held.
+    mServiceLock.unlock();
+
+    // Disconnect clients.
+    for (auto& clientSp : clientsToDisconnect) {
+        // We send up ERROR_CAMERA_DEVICE so that the app attempts to reconnect
+        // automatically.
+        clientSp->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE,
+                CaptureResultExtras{});
+        // This also triggers the status updates
+        clientSp->disconnect();
+    }
+
+    mServiceLock.lock();
+}
+
+std::vector<String8> CameraService::findOriginalIdsForRemappedCameraId(
+    const String8& inputCameraId, int clientUid) {
+    String16 packageName = getPackageNameFromUid(clientUid);
+    std::vector<String8> cameraIds;
+    Mutex::Autolock lock(mCameraIdRemappingLock);
+    if (auto packageMapIter = mCameraIdRemapping.find(packageName);
+        packageMapIter != mCameraIdRemapping.end()) {
+        for (auto& [id0, id1]: packageMapIter->second) {
+            if (id1 == inputCameraId) {
+                cameraIds.push_back(id0);
+            }
+        }
+    }
+    return cameraIds;
+}
+
+String8 CameraService::resolveCameraId(const String8& inputCameraId) {
+  return resolveCameraId(inputCameraId, String16(""));
+}
+
+String8 CameraService::resolveCameraId(
+    const String8& inputCameraId,
+    const String16& packageName) {
+    String16 packageNameVal = packageName;
+    if (packageName == String16("")) {
+        int clientUid = CameraThreadState::getCallingUid();
+        packageNameVal = getPackageNameFromUid(clientUid);
+    }
+    Mutex::Autolock lock(mCameraIdRemappingLock);
+    if (auto packageMapIter = mCameraIdRemapping.find(packageNameVal);
+        packageMapIter != mCameraIdRemapping.end()) {
+        ALOGI("%s: resolveCameraId: packageName found %s",
+                __FUNCTION__, String8(packageNameVal).c_str());
+        auto packageMap = packageMapIter->second;
+        if (auto replacementIdIter = packageMap.find(inputCameraId);
+            replacementIdIter != packageMap.end()) {
+            ALOGI("%s: resolveCameraId: inputId found %s, replacing with %s",
+                    __FUNCTION__, inputCameraId.c_str(),
+                    replacementIdIter->second.c_str());
+            return replacementIdIter->second;
+        }
+    }
+    return inputCameraId;
+}
+
 Status CameraService::getCameraInfo(int cameraId, bool overrideToPortrait,
         CameraInfo* cameraInfo) {
     ATRACE_CALL();
@@ -815,9 +963,10 @@
     return String8(cameraIdIntToStrLocked(cameraIdInt).c_str());
 }
 
-Status CameraService::getCameraCharacteristics(const String16& cameraId,
+Status CameraService::getCameraCharacteristics(const String16& unresolvedCameraId,
         int targetSdkVersion, bool overrideToPortrait, CameraMetadata* cameraInfo) {
     ATRACE_CALL();
+    String8 cameraId = resolveCameraId(String8(unresolvedCameraId));
     if (!cameraInfo) {
         ALOGE("%s: cameraInfo is NULL", __FUNCTION__);
         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "cameraInfo is NULL");
@@ -837,8 +986,7 @@
 
     Status ret{};
 
-
-    std::string cameraIdStr = String8(cameraId).string();
+    std::string cameraIdStr = cameraId.string();
     bool overrideForPerfClass =
             SessionConfigurationUtils::targetPerfClassPrimaryCamera(mPerfClassPrimaryCameraIds,
                     cameraIdStr, targetSdkVersion);
@@ -1028,7 +1176,7 @@
         int api1CameraId, int facing, int sensorOrientation, int clientPid, uid_t clientUid,
         int servicePid, std::pair<int, IPCTransport> deviceVersionAndTransport,
         apiLevel effectiveApiLevel, bool overrideForPerfClass, bool overrideToPortrait,
-        bool forceSlowJpegMode, /*out*/sp<BasicClient>* client) {
+        bool forceSlowJpegMode, const String8& originalCameraId, /*out*/sp<BasicClient>* client) {
     // For HIDL devices
     if (deviceVersionAndTransport.second == IPCTransport::HIDL) {
         // Create CameraClient based on device version reported by the HAL.
@@ -1071,7 +1219,7 @@
         *client = new CameraDeviceClient(cameraService, tmp,
                 cameraService->mCameraServiceProxyWrapper, packageName, systemNativeClient,
                 featureId, cameraId, facing, sensorOrientation, clientPid, clientUid, servicePid,
-                overrideForPerfClass, overrideToPortrait);
+                overrideForPerfClass, overrideToPortrait, originalCameraId);
         ALOGI("%s: Camera2 API, override to portrait %d", __FUNCTION__, overrideToPortrait);
     }
     return Status::ok();
@@ -1163,7 +1311,7 @@
             internalPackageName, /*systemNativeClient*/ false, {}, uid, USE_CALLING_PID,
             API_1, /*shimUpdateOnly*/ true, /*oomScoreOffset*/ 0,
             /*targetSdkVersion*/ __ANDROID_API_FUTURE__, /*overrideToPortrait*/ true,
-            /*forceSlowJpegMode*/false, /*out*/ tmp)
+            /*forceSlowJpegMode*/false, id, /*out*/ tmp)
             ).isOk()) {
         ALOGE("%s: Error initializing shim metadata: %s", __FUNCTION__, ret.toString8().string());
     }
@@ -1700,7 +1848,7 @@
     ret = connectHelper<ICameraClient,Client>(cameraClient, id, api1CameraId,
             clientPackageName,/*systemNativeClient*/ false, {}, clientUid, clientPid, API_1,
             /*shimUpdateOnly*/ false, /*oomScoreOffset*/ 0, targetSdkVersion,
-            overrideToPortrait, forceSlowJpegMode, /*out*/client);
+            overrideToPortrait, forceSlowJpegMode, id, /*out*/client);
 
     if(!ret.isOk()) {
         logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageName),
@@ -1780,7 +1928,7 @@
 
 Status CameraService::connectDevice(
         const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
-        const String16& cameraId,
+        const String16& unresolvedCameraId,
         const String16& clientPackageName,
         const std::optional<String16>& clientFeatureId,
         int clientUid, int oomScoreOffset, int targetSdkVersion,
@@ -1790,7 +1938,7 @@
 
     ATRACE_CALL();
     Status ret = Status::ok();
-    String8 id = String8(cameraId);
+    String8 id = resolveCameraId(String8(unresolvedCameraId), clientPackageName);
     sp<CameraDeviceClient> client = nullptr;
     String16 clientPackageNameAdj = clientPackageName;
     int callingPid = CameraThreadState::getCallingPid();
@@ -1840,7 +1988,7 @@
             /*api1CameraId*/-1, clientPackageNameAdj, systemNativeClient,clientFeatureId,
             clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, oomScoreOffset,
             targetSdkVersion, overrideToPortrait, /*forceSlowJpegMode*/false,
-            /*out*/client);
+            String8(unresolvedCameraId), /*out*/client);
 
     if(!ret.isOk()) {
         logRejected(id, callingPid, String8(clientPackageNameAdj), ret.toString8());
@@ -1909,7 +2057,7 @@
         int api1CameraId, const String16& clientPackageNameMaybe, bool systemNativeClient,
         const std::optional<String16>& clientFeatureId, int clientUid, int clientPid,
         apiLevel effectiveApiLevel, bool shimUpdateOnly, int oomScoreOffset, int targetSdkVersion,
-        bool overrideToPortrait, bool forceSlowJpegMode,
+        bool overrideToPortrait, bool forceSlowJpegMode, const String8& originalCameraId,
         /*out*/sp<CLIENT>& device) {
     binder::Status ret = binder::Status::ok();
 
@@ -2026,7 +2174,7 @@
                 clientFeatureId, cameraId, api1CameraId, facing, orientation,
                 clientPid, clientUid, getpid(),
                 deviceVersionAndTransport, effectiveApiLevel, overrideForPerfClass,
-                overrideToPortrait, forceSlowJpegMode,
+                overrideToPortrait, forceSlowJpegMode, originalCameraId,
                 /*out*/&tmp)).isOk()) {
             return ret;
         }
@@ -2281,7 +2429,8 @@
     return OK;
 }
 
-Status CameraService::turnOnTorchWithStrengthLevel(const String16& cameraId, int32_t torchStrength,
+Status CameraService::turnOnTorchWithStrengthLevel(const String16& unresolvedCameraId,
+        int32_t torchStrength,
         const sp<IBinder>& clientBinder) {
     Mutex::Autolock lock(mServiceLock);
 
@@ -2292,7 +2441,7 @@
                 "Torch client binder in null.");
     }
 
-    String8 id = String8(cameraId.string());
+    String8 id = resolveCameraId(String8(unresolvedCameraId));
     int uid = CameraThreadState::getCallingUid();
 
     if (shouldRejectSystemCameraConnection(id)) {
@@ -2352,7 +2501,7 @@
 
     {
         Mutex::Autolock al(mTorchUidMapMutex);
-        updateTorchUidMapLocked(cameraId, uid);
+        updateTorchUidMapLocked(String16(id), uid);
     }
     // Check if the current torch strength level is same as the new one.
     bool shouldSkipTorchStrengthUpdates = mCameraProviderManager->shouldSkipTorchStrengthUpdate(
@@ -2411,7 +2560,8 @@
     return Status::ok();
 }
 
-Status CameraService::setTorchMode(const String16& cameraId, bool enabled,
+Status CameraService::setTorchMode(const String16& unresolvedCameraId,
+        bool enabled,
         const sp<IBinder>& clientBinder) {
     Mutex::Autolock lock(mServiceLock);
 
@@ -2422,7 +2572,7 @@
                 "Torch client Binder is null");
     }
 
-    String8 id = String8(cameraId.string());
+    String8 id = resolveCameraId(String8(unresolvedCameraId));
     int uid = CameraThreadState::getCallingUid();
 
     if (shouldRejectSystemCameraConnection(id)) {
@@ -2482,7 +2632,7 @@
         // Update UID map - this is used in the torch status changed callbacks, so must be done
         // before setTorchMode
         Mutex::Autolock al(mTorchUidMapMutex);
-        updateTorchUidMapLocked(cameraId, uid);
+        updateTorchUidMapLocked(String16(id), uid);
     }
 
     status_t err = mFlashlight->setTorchMode(id, enabled);
@@ -2947,11 +3097,20 @@
     return ret;
 }
 
-Status CameraService::supportsCameraApi(const String16& cameraId, int apiVersion,
+Status CameraService::supportsCameraApi(const String16& unresolvedCameraId, int apiVersion,
         /*out*/ bool *isSupported) {
     ATRACE_CALL();
 
-    const String8 id = String8(cameraId);
+    String8 resolvedId;
+    if (apiVersion == API_VERSION_2) {
+        resolvedId = resolveCameraId(String8(unresolvedCameraId));
+    } else { // if (apiVersion == API_VERSION_1)
+        // We don't support remapping for API 1.
+        // TODO(b/286287541): Also support remapping for API 1.
+        resolvedId = String8(unresolvedCameraId);
+    }
+
+    const String8 id = resolvedId;
 
     ALOGV("%s: for camera ID = %s", __FUNCTION__, id.string());
 
@@ -3011,11 +3170,11 @@
     return Status::ok();
 }
 
-Status CameraService::isHiddenPhysicalCamera(const String16& cameraId,
+Status CameraService::isHiddenPhysicalCamera(const String16& unresolvedCameraId,
         /*out*/ bool *isSupported) {
     ATRACE_CALL();
 
-    const String8 id = String8(cameraId);
+    const String8 id = resolveCameraId(String8(unresolvedCameraId));
 
     ALOGV("%s: for camera ID = %s", __FUNCTION__, id.string());
     *isSupported = mCameraProviderManager->isHiddenPhysicalCamera(id.string());
@@ -4958,7 +5117,6 @@
     state->updateStatus(status, cameraId, rejectSourceStates, [this, &deviceKind,
                         &logicalCameraIds]
             (const String8& cameraId, StatusInternal status) {
-
             if (status != StatusInternal::ENUMERATING) {
                 // Update torch status if it has a flash unit.
                 Mutex::Autolock al(mTorchStatusMutex);
@@ -4991,9 +5149,21 @@
                 auto ret = listener->getListener()->onStatusChanged(mapToInterface(status),
                         String16(cameraId));
                 listener->handleBinderStatus(ret,
-                        "%s: Failed to trigger onStatusChanged callback for %d:%d: %d",
+                         "%s: Failed to trigger onStatusChanged callback for %d:%d: %d",
                         __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
                         ret.exceptionCode());
+                // Also trigger the callbacks for cameras that were remapped to the current
+                // cameraId for the specific package that this listener belongs to.
+                std::vector<String8> remappedCameraIds =
+                        findOriginalIdsForRemappedCameraId(cameraId, listener->getListenerUid());
+                for (auto& remappedCameraId : remappedCameraIds) {
+                    ret = listener->getListener()->onStatusChanged(
+                            mapToInterface(status), String16(remappedCameraId));
+                    listener->handleBinderStatus(ret,
+                             "%s: Failed to trigger onStatusChanged callback for %d:%d: %d",
+                            __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+                            ret.exceptionCode());
+                }
             }
         });
 }
@@ -5204,6 +5374,8 @@
         return handleWatchCommand(args, in, out);
     } else if (args.size() >= 2 && args[0] == String16("set-watchdog")) {
         return handleSetCameraServiceWatchdog(args);
+    } else if (args.size() >= 4 && args[0] == String16("remap-camera-id")) {
+        return handleCameraIdRemapping(args, err);
     } else if (args.size() == 1 && args[0] == String16("help")) {
         printHelp(out);
         return OK;
@@ -5212,6 +5384,23 @@
     return BAD_VALUE;
 }
 
+status_t CameraService::handleCameraIdRemapping(const Vector<String16>& args, int err) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid != AID_ROOT) {
+        dprintf(err, "Must be adb root\n");
+        return PERMISSION_DENIED;
+    }
+    if (args.size() != 4) {
+        dprintf(err, "Expected format: remap-camera-id <PACKAGE> <Id0> <Id1>\n");
+        return BAD_VALUE;
+    }
+    String16 packageName = args[1];
+    String8 cameraIdToReplace = String8(args[2]);
+    String8 cameraIdNew = String8(args[3]);
+    remapCameraIds({{packageName, {{cameraIdToReplace, cameraIdNew}}}});
+    return OK;
+}
+
 status_t CameraService::handleSetUidState(const Vector<String16>& args, int err) {
     String16 packageName = args[1];
 
@@ -5839,6 +6028,7 @@
         "  clear-stream-use-case-override clear the stream use case override\n"
         "  set-zoom-override <-1/0/1> enable or disable zoom override\n"
         "      Valid values -1: do not override, 0: override to OFF, 1: override to ZOOM\n"
+        "  remap-camera-id <PACKAGE> <Id0> <Id1> remaps camera ids. Must use adb root\n"
         "  watch <start|stop|dump|print|clear> manages tag monitoring in connected clients\n"
         "  help print this message\n");
 }
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 3214d4c..b9966b3 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -20,6 +20,7 @@
 #include <android/hardware/BnCameraService.h>
 #include <android/hardware/BnSensorPrivacyListener.h>
 #include <android/hardware/ICameraServiceListener.h>
+#include <android/hardware/CameraIdRemapping.h>
 #include <android/hardware/camera2/BnCameraInjectionSession.h>
 #include <android/hardware/camera2/ICameraInjectionCallback.h>
 
@@ -61,6 +62,7 @@
 #include <utility>
 #include <unordered_map>
 #include <unordered_set>
+#include <vector>
 
 namespace android {
 
@@ -138,6 +140,9 @@
 
     /////////////////////////////////////////////////////////////////////
     // ICameraService
+    // IMPORTANT: All binder calls that deal with logicalCameraId should use
+    // resolveCameraId(logicalCameraId) to arrive at the correct cameraId to
+    // perform the operation on (in case of Id Remapping).
     virtual binder::Status     getNumberOfCameras(int32_t type, int32_t* numCameras);
 
     virtual binder::Status     getCameraInfo(int cameraId, bool overrideToPortrait,
@@ -221,6 +226,9 @@
     virtual binder::Status reportExtensionSessionStats(
             const hardware::CameraExtensionSessionStats& stats, String16* sessionKey /*out*/);
 
+    virtual binder::Status remapCameraIds(const hardware::CameraIdRemapping&
+        cameraIdRemapping);
+
     // Extra permissions checks
     virtual status_t    onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags);
@@ -916,7 +924,7 @@
             int api1CameraId, const String16& clientPackageNameMaybe, bool systemNativeClient,
             const std::optional<String16>& clientFeatureId, int clientUid, int clientPid,
             apiLevel effectiveApiLevel, bool shimUpdateOnly, int scoreOffset, int targetSdkVersion,
-            bool overrideToPortrait, bool forceSlowJpegMode,
+            bool overrideToPortrait, bool forceSlowJpegMode, const String8& originalCameraId,
             /*out*/sp<CLIENT>& device);
 
     // Lock guarding camera service state
@@ -943,6 +951,48 @@
     // Mutex guarding mCameraStates map
     mutable Mutex mCameraStatesLock;
 
+    /**
+     * Mapping from packageName -> {cameraIdToReplace -> newCameraIdtoUse}.
+     *
+     * This specifies that for packageName, for every binder operation targeting
+     * cameraIdToReplace, use newCameraIdToUse instead.
+     */
+    typedef std::map<String16, std::map<String8, String8>> TCameraIdRemapping;
+    TCameraIdRemapping mCameraIdRemapping{};
+    /** Mutex guarding mCameraIdRemapping. */
+    Mutex mCameraIdRemappingLock;
+
+    /** Parses cameraIdRemapping parcelable into the native cameraIdRemappingMap. */
+    binder::Status parseCameraIdRemapping(
+        const hardware::CameraIdRemapping& cameraIdRemapping,
+        TCameraIdRemapping cameraIdRemappingMap);
+
+    /**
+     * Resolve the (potentially remapped) camera Id to use for packageName.
+     *
+     * This returns the Camera Id to use in case inputCameraId was remapped to a
+     * different Id for the given packageName. Otherwise, it returns the inputCameraId.
+     */
+    String8 resolveCameraId(const String8& inputCameraId, const String16& packageName);
+    /**
+     * Resolve the (potentially remapped) camera Id to use.
+     *
+     * This returns the Camera Id to use in case inputCameraId was remapped to a
+     * different Id for the packageName of the client. Otherwise, it returns the inputCameraId.
+     */
+    String8 resolveCameraId(const String8& inputCameraId);
+
+    /**
+     * Updates the state of mCameraIdRemapping, while disconnecting active clients as necessary.
+     */
+    void remapCameraIds(const TCameraIdRemapping& cameraIdRemapping);
+
+    /**
+     * Finds the Camera Ids that were remapped to the inputCameraId for the given client.
+     */
+    std::vector<String8> findOriginalIdsForRemappedCameraId(
+        const String8& inputCameraId, int clientUid);
+
     // Circular buffer for storing event logging for dumps
     RingBuffer<String8> mEventLog;
     Mutex mLogLock;
@@ -1322,6 +1372,9 @@
     // Set or clear the zoom override flag
     status_t handleSetZoomOverride(const Vector<String16>& args);
 
+    // Set Camera Id remapping using 'cmd'
+    status_t handleCameraIdRemapping(const Vector<String16>& args, int errFd);
+
     // Handle 'watch' command as passed through 'cmd'
     status_t handleWatchCommand(const Vector<String16> &args, int inFd, int outFd);
 
@@ -1367,14 +1420,15 @@
      */
     static String8 getFormattedCurrentTime();
 
-    static binder::Status makeClient(const sp<CameraService>& cameraService,
-            const sp<IInterface>& cameraCb, const String16& packageName,
-            bool systemNativeClient, const std::optional<String16>& featureId,
-            const String8& cameraId, int api1CameraId, int facing, int sensorOrientation,
-            int clientPid, uid_t clientUid, int servicePid,
+    static binder::Status makeClient(
+            const sp<CameraService>& cameraService, const sp<IInterface>& cameraCb,
+            const String16& packageName, bool systemNativeClient,
+            const std::optional<String16>& featureId, const String8& cameraId, int api1CameraId,
+            int facing, int sensorOrientation, int clientPid, uid_t clientUid, int servicePid,
             std::pair<int, IPCTransport> deviceVersionAndIPCTransport, apiLevel effectiveApiLevel,
             bool overrideForPerfClass, bool overrideToPortrait, bool forceSlowJpegMode,
-            /*out*/sp<BasicClient>* client);
+            const String8& originalCameraId,
+            /*out*/ sp<BasicClient>* client);
 
     status_t checkCameraAccess(const String16& opPackageName);
 
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 38c615d..1720b55 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -98,7 +98,8 @@
         uid_t clientUid,
         int servicePid,
         bool overrideForPerfClass,
-        bool overrideToPortrait) :
+        bool overrideToPortrait,
+        const String8& originalCameraId) :
     Camera2ClientBase(cameraService, remoteCallback, cameraServiceProxyWrapper, clientPackageName,
             systemNativeClient, clientFeatureId, cameraId, /*API1 camera ID*/ -1, cameraFacing,
             sensorOrientation, clientPid, clientUid, servicePid, overrideForPerfClass,
@@ -106,8 +107,8 @@
     mInputStream(),
     mStreamingRequestId(REQUEST_ID_NONE),
     mRequestIdCounter(0),
-    mOverrideForPerfClass(overrideForPerfClass) {
-
+    mOverrideForPerfClass(overrideForPerfClass),
+    mOriginalCameraId(originalCameraId) {
     ATRACE_CALL();
     ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
 }
@@ -322,7 +323,7 @@
 
         //The first capture settings should always match the logical camera id
         String8 logicalId(request.mPhysicalCameraSettings.begin()->id.c_str());
-        if (mDevice->getId() != logicalId) {
+        if (mDevice->getId() != logicalId && mOriginalCameraId != logicalId) {
             ALOGE("%s: Camera %s: Invalid camera request settings.", __FUNCTION__,
                     mCameraIdStr.string());
             return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
@@ -437,6 +438,8 @@
 
         CameraDeviceBase::PhysicalCameraSettingsList physicalSettingsList;
         for (const auto& it : request.mPhysicalCameraSettings) {
+            std::string resolvedId = (
+                mOriginalCameraId.string() == it.id) ? mDevice->getId().string() : it.id;
             if (it.settings.isEmpty()) {
                 ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",
                         __FUNCTION__, mCameraIdStr.string());
@@ -447,7 +450,7 @@
             // Check whether the physical / logical stream has settings
             // consistent with the sensor pixel mode(s) it was configured with.
             // mCameraIdToStreamSet will only have ids that are high resolution
-            const auto streamIdSetIt = mHighResolutionCameraIdToStreamIdSet.find(it.id);
+            const auto streamIdSetIt = mHighResolutionCameraIdToStreamIdSet.find(resolvedId);
             if (streamIdSetIt != mHighResolutionCameraIdToStreamIdSet.end()) {
                 std::list<int> streamIdsUsedInRequest = getIntersection(streamIdSetIt->second,
                         outputStreamIds);
@@ -455,14 +458,14 @@
                         !isSensorPixelModeConsistent(streamIdsUsedInRequest, it.settings)) {
                      ALOGE("%s: Camera %s: Request settings CONTROL_SENSOR_PIXEL_MODE not "
                             "consistent with configured streams. Rejecting request.",
-                            __FUNCTION__, it.id.c_str());
+                            __FUNCTION__, resolvedId.c_str());
                     return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
                         "Request settings CONTROL_SENSOR_PIXEL_MODE are not consistent with "
                         "streams configured");
                 }
             }
 
-            String8 physicalId(it.id.c_str());
+            String8 physicalId(resolvedId.c_str());
             bool hasTestPatternModePhysicalKey = std::find(mSupportedPhysicalRequestKeys.begin(),
                     mSupportedPhysicalRequestKeys.end(), ANDROID_SENSOR_TEST_PATTERN_MODE) !=
                     mSupportedPhysicalRequestKeys.end();
@@ -471,7 +474,7 @@
                     mSupportedPhysicalRequestKeys.end();
             if (physicalId != mDevice->getId()) {
                 auto found = std::find(requestedPhysicalIds.begin(), requestedPhysicalIds.end(),
-                        it.id);
+                        resolvedId);
                 if (found == requestedPhysicalIds.end()) {
                     ALOGE("%s: Camera %s: Physical camera id: %s not part of attached outputs.",
                             __FUNCTION__, mCameraIdStr.string(), physicalId.string());
@@ -494,11 +497,11 @@
                         }
                     }
 
-                    physicalSettingsList.push_back({it.id, filteredParams,
+                    physicalSettingsList.push_back({resolvedId, filteredParams,
                             hasTestPatternModePhysicalKey, hasTestPatternDataPhysicalKey});
                 }
             } else {
-                physicalSettingsList.push_back({it.id, it.settings});
+                physicalSettingsList.push_back({resolvedId, it.settings});
             }
         }
 
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 1533cf5..95563ee 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -191,7 +191,8 @@
             uid_t clientUid,
             int servicePid,
             bool overrideForPerfClass,
-            bool overrideToPortrait);
+            bool overrideToPortrait,
+            const String8& originalCameraId);
     virtual ~CameraDeviceClient();
 
     virtual status_t      initialize(sp<CameraProviderManager> manager,
@@ -368,6 +369,9 @@
     std::string mUserTag;
     // The last set video stabilization mode
     int mVideoStabilizationMode = -1;
+
+    // This only exists in case of camera ID Remapping.
+    String8 mOriginalCameraId;
 };
 
 }; // namespace android