Add ID argument to enable_test_camera shell cmd

Allows for more flexibility and consistency in test scenarios. The argument is optional. If not provided it falls back to the current camera id generation scheme.

Bug: 322965201
Test: adb shell cmd virtual_camera enable_test_camera 99999

Change-Id: I196406b040322022a8f6f3a43e1fc4709a244dee
diff --git a/services/camera/virtualcamera/VirtualCameraProvider.cc b/services/camera/virtualcamera/VirtualCameraProvider.cc
index e4a68f5..eed3e85 100644
--- a/services/camera/virtualcamera/VirtualCameraProvider.cc
+++ b/services/camera/virtualcamera/VirtualCameraProvider.cc
@@ -42,10 +42,6 @@
 using ::aidl::android::hardware::camera::provider::ConcurrentCameraIdCombination;
 using ::aidl::android::hardware::camera::provider::ICameraProviderCallback;
 
-// TODO(b/301023410) Make camera id range configurable / dynamic
-// based on already registered devices.
-std::atomic_int VirtualCameraProvider::sNextId{42};
-
 ndk::ScopedAStatus VirtualCameraProvider::setCallback(
     const std::shared_ptr<ICameraProviderCallback>& in_callback) {
   ALOGV("%s", __func__);
@@ -154,9 +150,15 @@
 }
 
 std::shared_ptr<VirtualCameraDevice> VirtualCameraProvider::createCamera(
-    const VirtualCameraConfiguration& configuration) {
+    const VirtualCameraConfiguration& configuration, const int cameraId) {
+  if (cameraId < 0) {
+    ALOGE("%s: Cannot create camera with negative id. cameraId: %d", __func__,
+          cameraId);
+    return nullptr;
+  }
+
   auto camera =
-      ndk::SharedRefBase::make<VirtualCameraDevice>(sNextId++, configuration);
+      ndk::SharedRefBase::make<VirtualCameraDevice>(cameraId, configuration);
   std::shared_ptr<ICameraProviderCallback> callback;
   {
     const std::lock_guard<std::mutex> lock(mLock);
diff --git a/services/camera/virtualcamera/VirtualCameraProvider.h b/services/camera/virtualcamera/VirtualCameraProvider.h
index 11d3123..c1283a0 100644
--- a/services/camera/virtualcamera/VirtualCameraProvider.h
+++ b/services/camera/virtualcamera/VirtualCameraProvider.h
@@ -76,7 +76,8 @@
   // Returns nullptr if creation was not successful.
   std::shared_ptr<VirtualCameraDevice> createCamera(
       const aidl::android::companion::virtualcamera::VirtualCameraConfiguration&
-          configuration);
+          configuration,
+      int cameraId);
 
   std::shared_ptr<VirtualCameraDevice> getCamera(const std::string& name);
 
@@ -91,9 +92,6 @@
 
   std::map<std::string, std::shared_ptr<VirtualCameraDevice>> mCameras
       GUARDED_BY(mLock);
-
-  // Numerical id to assign to next created camera.
-  static std::atomic_int sNextId;
 };
 
 }  // namespace virtualcamera
diff --git a/services/camera/virtualcamera/VirtualCameraService.cc b/services/camera/virtualcamera/VirtualCameraService.cc
index 1144997..0c72388 100644
--- a/services/camera/virtualcamera/VirtualCameraService.cc
+++ b/services/camera/virtualcamera/VirtualCameraService.cc
@@ -46,6 +46,10 @@
 using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
 using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
 
+// TODO(b/301023410) Make camera id range configurable / dynamic
+// based on already registered devices.
+std::atomic_int VirtualCameraService::sNextId{1000};
+
 namespace {
 
 constexpr int kVgaWidth = 640;
@@ -110,6 +114,13 @@
 ndk::ScopedAStatus VirtualCameraService::registerCamera(
     const ::ndk::SpAIBinder& token,
     const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
+  return registerCamera(token, configuration, sNextId++, _aidl_return);
+}
+
+ndk::ScopedAStatus VirtualCameraService::registerCamera(
+    const ::ndk::SpAIBinder& token,
+    const VirtualCameraConfiguration& configuration, const int cameraId,
+    bool* _aidl_return) {
   if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
     ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
           getpid(), getuid(), kCreateVirtualDevicePermission);
@@ -141,7 +152,7 @@
   }
 
   std::shared_ptr<VirtualCameraDevice> camera =
-      mVirtualCameraProvider->createCamera(configuration);
+      mVirtualCameraProvider->createCamera(configuration, cameraId);
   if (camera == nullptr) {
     ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
           reinterpret_cast<uintptr_t>(token.get()));
@@ -179,7 +190,7 @@
 }
 
 ndk::ScopedAStatus VirtualCameraService::getCameraId(
-        const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
+    const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
   if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
     ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
           getpid(), getuid(), kCreateVirtualDevicePermission);
@@ -188,7 +199,7 @@
 
   if (_aidl_return == nullptr) {
     return ndk::ScopedAStatus::fromServiceSpecificError(
-            Status::EX_ILLEGAL_ARGUMENT);
+        Status::EX_ILLEGAL_ARGUMENT);
   }
 
   auto camera = getCamera(token);
@@ -235,7 +246,15 @@
   }
   const char* const cmd = args[0];
   if (strcmp(kEnableTestCameraCmd, cmd) == 0) {
-    enableTestCameraCmd(in, err);
+    int cameraId = 0;
+    if (numArgs > 1 && args[1] != nullptr) {
+      cameraId = atoi(args[1]);
+    }
+    if (cameraId == 0) {
+      cameraId = sNextId++;
+    }
+
+    enableTestCameraCmd(in, err, cameraId);
   } else if (strcmp(kDisableTestCameraCmd, cmd) == 0) {
     disableTestCameraCmd(in);
   } else {
@@ -246,7 +265,8 @@
   return STATUS_OK;
 }
 
-void VirtualCameraService::enableTestCameraCmd(const int out, const int err) {
+void VirtualCameraService::enableTestCameraCmd(const int out, const int err,
+                                               const int cameraId) {
   if (mTestCameraToken != nullptr) {
     dprintf(out, "Test camera is already enabled (%s).",
             getCamera(mTestCameraToken)->getCameraName().c_str());
@@ -263,7 +283,7 @@
                                                   Format::YUV_420_888,
                                                   .maxFps = kMaxFps});
   configuration.lensFacing = LensFacing::EXTERNAL;
-  registerCamera(mTestCameraToken, configuration, &ret);
+  registerCamera(mTestCameraToken, configuration, cameraId, &ret);
   if (ret) {
     dprintf(out, "Successfully registered test camera %s",
             getCamera(mTestCameraToken)->getCameraName().c_str());
diff --git a/services/camera/virtualcamera/VirtualCameraService.h b/services/camera/virtualcamera/VirtualCameraService.h
index d573986..d447fc7 100644
--- a/services/camera/virtualcamera/VirtualCameraService.h
+++ b/services/camera/virtualcamera/VirtualCameraService.h
@@ -45,6 +45,13 @@
           configuration,
       bool* _aidl_return) override EXCLUDES(mLock);
 
+  // Register camera corresponding to the binder token.
+  ndk::ScopedAStatus registerCamera(
+      const ::ndk::SpAIBinder& token,
+      const ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration&
+          configuration,
+      int cameraId, bool* _aidl_return) EXCLUDES(mLock);
+
   // Unregisters camera corresponding to the binder token.
   ndk::ScopedAStatus unregisterCamera(const ::ndk::SpAIBinder& token) override
       EXCLUDES(mLock);
@@ -64,7 +71,7 @@
 
  private:
   // Create and enable test camera instance if there's none.
-  void enableTestCameraCmd(int out, int err);
+  void enableTestCameraCmd(int out, int err, int cameraId);
   // Disable and destroy test camera instance if there's one.
   void disableTestCameraCmd(int out);
 
@@ -84,6 +91,9 @@
 
   // Local binder token for test camera instance, or nullptr if there's none.
   ::ndk::SpAIBinder mTestCameraToken;
+
+  // Numerical id to assign to next created camera.
+  static std::atomic_int sNextId;
 };
 
 }  // namespace virtualcamera
diff --git a/services/camera/virtualcamera/tests/VirtualCameraProviderTest.cc b/services/camera/virtualcamera/tests/VirtualCameraProviderTest.cc
index ab647a4..cd64ca5 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraProviderTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraProviderTest.cc
@@ -53,6 +53,7 @@
 constexpr int kVgaWidth = 640;
 constexpr int kVgaHeight = 480;
 constexpr int kMaxFps = 30;
+constexpr int kCameraId = 9999;
 constexpr char kVirtualCameraNameRegex[] =
     "device@[0-9]+\\.[0-9]+/virtual/[0-9]+";
 
@@ -118,7 +119,7 @@
 
   ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk());
   std::shared_ptr<VirtualCameraDevice> camera =
-      mCameraProvider->createCamera(mInputConfig);
+      mCameraProvider->createCamera(mInputConfig, kCameraId);
   EXPECT_THAT(camera, Not(IsNull()));
   EXPECT_THAT(camera->getCameraName(), MatchesRegex(kVirtualCameraNameRegex));
 
@@ -136,7 +137,7 @@
       .WillOnce(Return(ndk::ScopedAStatus::ok()));
 
   std::shared_ptr<VirtualCameraDevice> camera =
-      mCameraProvider->createCamera(mInputConfig);
+      mCameraProvider->createCamera(mInputConfig, kCameraId);
   ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk());
 
   // Created camera should be in the list of cameras.
@@ -148,7 +149,7 @@
 TEST_F(VirtualCameraProviderTest, RemoveCamera) {
   ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk());
   std::shared_ptr<VirtualCameraDevice> camera =
-      mCameraProvider->createCamera(mInputConfig);
+      mCameraProvider->createCamera(mInputConfig, kCameraId);
 
   EXPECT_CALL(*mMockCameraProviderCallback,
               cameraDeviceStatusChange(Eq(camera->getCameraName()),
@@ -165,7 +166,7 @@
 TEST_F(VirtualCameraProviderTest, RemoveNonExistingCamera) {
   ASSERT_TRUE(mCameraProvider->setCallback(mMockCameraProviderCallback).isOk());
   std::shared_ptr<VirtualCameraDevice> camera =
-      mCameraProvider->createCamera(mInputConfig);
+      mCameraProvider->createCamera(mInputConfig, kCameraId);
 
   // Removing non-existing camera should fail.
   const std::string cameraName = "DefinitelyNoTCamera";
diff --git a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
index d4d00a2..12a09f5 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
@@ -142,6 +142,14 @@
         Eq(NO_ERROR));
   }
 
+  void execute_shell_command(const std::string cmd, const std::string cameraId) {
+    std::array<const char*, 2> args{cmd.data(), cameraId.data()};
+    ASSERT_THAT(
+        mCameraService->handleShellCommand(mDevNullFd, mDevNullFd, mDevNullFd,
+                                           args.data(), args.size()),
+        Eq(NO_ERROR));
+  }
+
   std::vector<std::string> getCameraIds() {
     std::vector<std::string> cameraIds;
     EXPECT_TRUE(mCameraProvider->getCameraIdList(&cameraIds).isOk());
@@ -381,6 +389,18 @@
   EXPECT_THAT(cameraIdsAfterDisable, IsEmpty());
 }
 
+TEST_F(VirtualCameraServiceTest, TestCameraShellCmdWithId) {
+  execute_shell_command("enable_test_camera", "12345");
+
+  std::vector<std::string> cameraIdsAfterEnable = getCameraIds();
+  EXPECT_THAT(cameraIdsAfterEnable, SizeIs(1));
+
+  execute_shell_command("disable_test_camera");
+
+  std::vector<std::string> cameraIdsAfterDisable = getCameraIds();
+  EXPECT_THAT(cameraIdsAfterDisable, IsEmpty());
+}
+
 }  // namespace
 }  // namespace virtualcamera
 }  // namespace companion