DynamicCamera: Add new hidden APIs in AIDL and CameraService for injection camera
- Add new hidden APIs: injectCamera() and stopInjection() in AIDL and
Camera Service.
- Add the InjectionStatusListener class to detect the death of the
callback interface binder signal process, and then the camera service
can kill the injection camera.
- Implement the hardware::camera2::BnCameraInjectionSession interface.
Bug: 171021010
Test: Verified camera service use cases function as expected; no denials
Change-Id: I81850502c15942c3b03bc8da289f0b67a51c7fcb
diff --git a/camera/Android.bp b/camera/Android.bp
index 2c01496..6878c20 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -119,6 +119,8 @@
"aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl",
"aidl/android/hardware/camera2/ICameraDeviceUser.aidl",
"aidl/android/hardware/camera2/ICameraOfflineSession.aidl",
+ "aidl/android/hardware/camera2/ICameraInjectionCallback.aidl",
+ "aidl/android/hardware/camera2/ICameraInjectionSession.aidl",
],
path: "aidl",
}
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 459ad15..873d738 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -20,6 +20,8 @@
import android.hardware.ICameraClient;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraInjectionCallback;
+import android.hardware.camera2.ICameraInjectionSession;
import android.hardware.camera2.params.VendorTagDescriptor;
import android.hardware.camera2.params.VendorTagDescriptorCache;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
@@ -161,6 +163,9 @@
boolean supportsCameraApi(String cameraId, int apiVersion);
// Determines if a cameraId is a hidden physical camera of a logical multi-camera.
boolean isHiddenPhysicalCamera(String cameraId);
+ // Inject the external camera to replace the internal camera session.
+ ICameraInjectionSession injectCamera(String packageName, String internalCamId,
+ String externalCamId, in ICameraInjectionCallback CameraInjectionCallback);
void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder);
diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl
new file mode 100644
index 0000000..9791352
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.camera2;
+
+import android.hardware.camera2.ICameraInjectionSession;
+
+/**
+ * Binder interface used to call back the error state injected by the external camera,
+ * and camera service can be switched back to internal camera when binder signals process death.
+ *
+ * @hide
+ */
+interface ICameraInjectionCallback
+{
+ // Error codes for onInjectionError
+ // To indicate all invalid error codes
+ const int ERROR_INJECTION_INVALID_ERROR = -1;
+ // To indicate the camera injection session has encountered a fatal error, such as injection
+ // init failure, configure failure or injecting failure etc.
+ const int ERROR_INJECTION_SESSION = 0;
+ // To indicate the camera service has encountered a fatal error.
+ const int ERROR_INJECTION_SERVICE = 1;
+ // To indicate the injection camera does not support certain camera functions, such as
+ // unsupport stream format, no capture/record function or no multi-camera function etc.
+ // When this error occurs, the default processing is still in the inject state, and the app is
+ // notified to display an error message and a black screen.
+ const int ERROR_INJECTION_UNSUPPORTED = 2;
+
+ oneway void onInjectionError(int errorCode);
+}
diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl
new file mode 100644
index 0000000..c31c30b
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 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.camera2;
+
+/** @hide */
+interface ICameraInjectionSession
+{
+ oneway void stopInjection();
+}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 65f27b1..3e6a7c7 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -91,6 +91,8 @@
using hardware::ICameraServiceListener;
using hardware::camera::common::V1_0::CameraDeviceStatus;
using hardware::camera::common::V1_0::TorchModeStatus;
+using hardware::camera2::ICameraInjectionCallback;
+using hardware::camera2::ICameraInjectionSession;
using hardware::camera2::utils::CameraIdAndSessionConfiguration;
using hardware::camera2::utils::ConcurrentCameraIdCombination;
@@ -127,6 +129,8 @@
sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
static const String16 sCameraOpenCloseListenerPermission(
"android.permission.CAMERA_OPEN_CLOSE_LISTENER");
+static const String16
+ sCameraInjectExternalCameraPermission("android.permission.CAMERA_INJECT_EXTERNAL_CAMERA");
static constexpr int32_t kVendorClientScore = resource_policy::PERCEPTIBLE_APP_ADJ;
static constexpr int32_t kVendorClientState = ActivityManager::PROCESS_STATE_PERSISTENT_UI;
@@ -166,6 +170,7 @@
mUidPolicy->registerSelf();
mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
mSensorPrivacyPolicy->registerSelf();
+ mInjectionStatusListener = new InjectionStatusListener(this);
mAppOps.setCameraAudioRestriction(mAudioRestriction);
sp<HidlCameraService> hcs = HidlCameraService::getInstance(this);
if (hcs->registerAsService() != android::OK) {
@@ -243,6 +248,7 @@
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
mUidPolicy->unregisterSelf();
mSensorPrivacyPolicy->unregisterSelf();
+ mInjectionStatusListener->removeListener();
}
void CameraService::onNewProviderRegistered() {
@@ -2386,6 +2392,42 @@
return Status::ok();
}
+Status CameraService::injectCamera(
+ const String16& packageName, const String16& internalCamId,
+ const String16& externalCamId,
+ const sp<ICameraInjectionCallback>& callback,
+ /*out*/
+ sp<hardware::camera2::ICameraInjectionSession>* cameraInjectionSession) {
+ ATRACE_CALL();
+
+ if (!checkCallingPermission(sCameraInjectExternalCameraPermission)) {
+ const int pid = CameraThreadState::getCallingPid();
+ const int uid = CameraThreadState::getCallingUid();
+ ALOGE("Permission Denial: can't inject camera pid=%d, uid=%d", pid, uid);
+ return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+ "Permission Denial: no permission to inject camera");
+ }
+
+ ALOGV(
+ "%s: Package name = %s, Internal camera ID = %s, External camera ID = "
+ "%s",
+ __FUNCTION__, String8(packageName).string(),
+ String8(internalCamId).string(), String8(externalCamId).string());
+
+ binder::Status ret = binder::Status::ok();
+ // TODO: Implement the injection camera function.
+ // ret = internalInjectCamera(...);
+ // if(!ret.isOk()) {
+ // mInjectionStatusListener->notifyInjectionError(...);
+ // return ret;
+ // }
+
+ mInjectionStatusListener->addListener(callback);
+ *cameraInjectionSession = new CameraInjectionSession(this);
+
+ return ret;
+}
+
void CameraService::removeByClient(const BasicClient* client) {
Mutex::Autolock lock(mServiceLock);
for (auto& i : mActiveClientManager.getAll()) {
@@ -3614,6 +3656,66 @@
}
// ----------------------------------------------------------------------------
+// InjectionStatusListener
+// ----------------------------------------------------------------------------
+
+void CameraService::InjectionStatusListener::addListener(
+ const sp<ICameraInjectionCallback>& callback) {
+ Mutex::Autolock lock(mListenerLock);
+ if (mCameraInjectionCallback) return;
+ status_t res = IInterface::asBinder(callback)->linkToDeath(this);
+ if (res == OK) {
+ mCameraInjectionCallback = callback;
+ }
+}
+
+void CameraService::InjectionStatusListener::removeListener() {
+ Mutex::Autolock lock(mListenerLock);
+ if (mCameraInjectionCallback == nullptr) {
+ ALOGW("InjectionStatusListener: mCameraInjectionCallback == nullptr");
+ return;
+ }
+ IInterface::asBinder(mCameraInjectionCallback)->unlinkToDeath(this);
+ mCameraInjectionCallback = nullptr;
+}
+
+void CameraService::InjectionStatusListener::notifyInjectionError(
+ int errorCode) {
+ Mutex::Autolock lock(mListenerLock);
+ if (mCameraInjectionCallback == nullptr) {
+ ALOGW("InjectionStatusListener: mCameraInjectionCallback == nullptr");
+ return;
+ }
+ mCameraInjectionCallback->onInjectionError(errorCode);
+}
+
+void CameraService::InjectionStatusListener::binderDied(
+ const wp<IBinder>& /*who*/) {
+ Mutex::Autolock lock(mListenerLock);
+ ALOGV("InjectionStatusListener: ICameraInjectionCallback has died");
+ auto parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->stopInjectionImpl();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// CameraInjectionSession
+// ----------------------------------------------------------------------------
+
+binder::Status CameraService::CameraInjectionSession::stopInjection() {
+ Mutex::Autolock lock(mInjectionSessionLock);
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("CameraInjectionSession: Parent is gone");
+ return STATUS_ERROR(ICameraInjectionCallback::ERROR_INJECTION_SERVICE,
+ "Camera service encountered error");
+ }
+ parent->stopInjectionImpl();
+ return binder::Status::ok();
+}
+
+// ----------------------------------------------------------------------------
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 60000;
@@ -4273,4 +4375,10 @@
return mode;
}
+void CameraService::stopInjectionImpl() {
+ mInjectionStatusListener->removeListener();
+
+ // TODO: Implement the stop injection function.
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index b436cec..10e1748 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -20,6 +20,8 @@
#include <android/hardware/BnCameraService.h>
#include <android/hardware/BnSensorPrivacyListener.h>
#include <android/hardware/ICameraServiceListener.h>
+#include <android/hardware/camera2/BnCameraInjectionSession.h>
+#include <android/hardware/camera2/ICameraInjectionCallback.h>
#include <cutils/multiuser.h>
#include <utils/Vector.h>
@@ -180,6 +182,13 @@
/*out*/
bool *isSupported);
+ virtual binder::Status injectCamera(
+ const String16& packageName, const String16& internalCamId,
+ const String16& externalCamId,
+ const sp<hardware::camera2::ICameraInjectionCallback>& callback,
+ /*out*/
+ sp<hardware::camera2::ICameraInjectionSession>* cameraInjectionSession);
+
// Extra permissions checks
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
@@ -1147,6 +1156,46 @@
// Current camera mute mode
bool mOverrideCameraMuteMode = false;
+
+ /**
+ * A listener class that implements the IBinder::DeathRecipient interface
+ * for use to call back the error state injected by the external camera, and
+ * camera service can kill the injection when binder signals process death.
+ */
+ class InjectionStatusListener : public virtual IBinder::DeathRecipient {
+ public:
+ InjectionStatusListener(sp<CameraService> parent) : mParent(parent) {}
+
+ void addListener(const sp<hardware::camera2::ICameraInjectionCallback>& callback);
+ void removeListener();
+ void notifyInjectionError(int errorCode);
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ Mutex mListenerLock;
+ wp<CameraService> mParent;
+ sp<hardware::camera2::ICameraInjectionCallback> mCameraInjectionCallback;
+ };
+
+ sp<InjectionStatusListener> mInjectionStatusListener;
+
+ /**
+ * A class that implements the hardware::camera2::BnCameraInjectionSession interface
+ */
+ class CameraInjectionSession : public hardware::camera2::BnCameraInjectionSession {
+ public:
+ CameraInjectionSession(sp<CameraService> parent) : mParent(parent) {}
+ virtual ~CameraInjectionSession() {}
+ binder::Status stopInjection() override;
+
+ private:
+ Mutex mInjectionSessionLock;
+ wp<CameraService> mParent;
+ };
+
+ void stopInjectionImpl();
};
} // namespace android