Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8cae10b authored by Cliff Wu's avatar Cliff Wu
Browse files

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
parent 18f1cf2c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -119,6 +119,8 @@ filegroup {
        "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",
}
+5 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.hardware.ICamera;
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 @@ interface ICameraService
    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);

+44 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+23 −0
Original line number Diff line number Diff line
/*
 * 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();
}
+108 −0
Original line number Diff line number Diff line
@@ -91,6 +91,8 @@ using hardware::ICameraClient;
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 @@ static const String16
        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 @@ void CameraService::onFirstRef()
    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 @@ CameraService::~CameraService() {
    VendorTagDescriptor::clearGlobalVendorTagDescriptor();
    mUidPolicy->unregisterSelf();
    mSensorPrivacyPolicy->unregisterSelf();
    mInjectionStatusListener->removeListener();
}

void CameraService::onNewProviderRegistered() {
@@ -2386,6 +2392,42 @@ Status CameraService::isHiddenPhysicalCamera(const String16& cameraId,
    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()) {
@@ -3613,6 +3655,66 @@ CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescr
            partial->getOwnerId(), partial->getPriority().getState());
}

// ----------------------------------------------------------------------------
//                  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;
@@ -4273,4 +4375,10 @@ int32_t CameraService::updateAudioRestrictionLocked() {
    return mode;
}

void CameraService::stopInjectionImpl() {
    mInjectionStatusListener->removeListener();

    // TODO: Implement the stop injection function.
}

}; // namespace android
Loading