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

Commit 178e823b authored by Eino-Ville Talvala's avatar Eino-Ville Talvala
Browse files

Camera: Move app ops to start/stop of streaming

A camera can be open but not streaming data. While most applications
immediately start streaming data, it can be useful to simply open the
camera early to hide some of the startup latency.

Historically, the camera service has reported camera app ops as
starting when the device is open, and stopping when the device closes.
With camera app op state becoming more directly visible to end users
via camera muting and camera usage notifications, it's useful to
narrow the scope of appops reporting to when the camera is actually
streaming frames, which is the time that's privacy-sensitive.

Here, the device-level status tracker is used to report idle and
active state to the client class, which then reports to appops
accordingly.  The same source is used for both idle and active to
ensure that there's no race condition possible that might cause
desynchronization of client state.

Bug: 185798362
Test: Manual testing with TestingCamera1 and 2, Camera CTS continues to pass
Change-Id: Ie2537e66603acfead20d69cf0fc5299462c0e9e4
parent 17879bb4
Loading
Loading
Loading
Loading
+92 −33
Original line number Diff line number Diff line
@@ -2770,19 +2770,20 @@ CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,
        const String8& cameraIdStr, int cameraFacing, int sensorOrientation,
        int clientPid, uid_t clientUid,
        int servicePid):
        mDestructionStarted(false),
        mCameraIdStr(cameraIdStr), mCameraFacing(cameraFacing), mOrientation(sensorOrientation),
        mClientPackageName(clientPackageName), mClientFeatureId(clientFeatureId),
        mClientPid(clientPid), mClientUid(clientUid),
        mServicePid(servicePid),
        mDisconnected(false), mUidIsTrusted(false),
        mAudioRestriction(hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE),
        mRemoteBinder(remoteCallback)
        mRemoteBinder(remoteCallback),
        mOpsActive(false),
        mOpsStreaming(false)
{
    if (sCameraService == nullptr) {
        sCameraService = cameraService;
    }
    mOpsActive = false;
    mDestructionStarted = false;

    // In some cases the calling code has no access to the package it runs under.
    // For example, NDK camera API.
@@ -2917,32 +2918,14 @@ bool CameraService::BasicClient::isValidAudioRestriction(int32_t mode) {
    }
}

status_t CameraService::BasicClient::startCameraOps() {
    ATRACE_CALL();

    {
        ALOGV("%s: Start camera ops, package name = %s, client UID = %d",
              __FUNCTION__, String8(mClientPackageName).string(), mClientUid);
    }
    if (mAppOpsManager != nullptr) {
        // Notify app ops that the camera is not available
        mOpsCallback = new OpsCallback(this);
        int32_t res;
        mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
                mClientPackageName, mOpsCallback);
        res = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA, mClientUid,
                mClientPackageName, /*startIfModeDefault*/ false, mClientFeatureId,
                String16("start camera ") + String16(mCameraIdStr));

        if (res == AppOpsManager::MODE_ERRORED) {
status_t CameraService::BasicClient::handleAppOpMode(int32_t mode) {
    if (mode == AppOpsManager::MODE_ERRORED) {
        ALOGI("Camera %s: Access for \"%s\" has been revoked",
                mCameraIdStr.string(), String8(mClientPackageName).string());
        return PERMISSION_DENIED;
        }

    } else if (!mUidIsTrusted && mode == AppOpsManager::MODE_IGNORED) {
        // If the calling Uid is trusted (a native service), the AppOpsManager could
        // return MODE_IGNORED. Do not treat such case as error.
        if (!mUidIsTrusted && res == AppOpsManager::MODE_IGNORED) {
        bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid,
                mClientPackageName);
        bool isCameraPrivacyEnabled =
@@ -2955,6 +2938,30 @@ status_t CameraService::BasicClient::startCameraOps() {
            return -EACCES;
        }
    }
    return OK;
}

status_t CameraService::BasicClient::startCameraOps() {
    ATRACE_CALL();

    {
        ALOGV("%s: Start camera ops, package name = %s, client UID = %d",
              __FUNCTION__, String8(mClientPackageName).string(), mClientUid);
    }
    if (mAppOpsManager != nullptr) {
        // Notify app ops that the camera is not available
        mOpsCallback = new OpsCallback(this);
        mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
                mClientPackageName, mOpsCallback);

        // Just check for camera acccess here on open - delay startOp until
        // camera frames start streaming in startCameraStreamingOps
        int32_t mode = mAppOpsManager->checkOp(AppOpsManager::OP_CAMERA, mClientUid,
                mClientPackageName);
        status_t res = handleAppOpMode(mode);
        if (res != OK) {
            return res;
        }
    }

    mOpsActive = true;
@@ -2970,17 +2977,69 @@ status_t CameraService::BasicClient::startCameraOps() {
    return OK;
}

status_t CameraService::BasicClient::finishCameraOps() {
status_t CameraService::BasicClient::startCameraStreamingOps() {
    ATRACE_CALL();

    // Check if startCameraOps succeeded, and if so, finish the camera op
    if (mOpsActive) {
        // Notify app ops that the camera is available again
    if (!mOpsActive) {
        ALOGE("%s: Calling streaming start when not yet active", __FUNCTION__);
        return INVALID_OPERATION;
    }
    if (mOpsStreaming) {
        ALOGV("%s: Streaming already active!", __FUNCTION__);
        return OK;
    }

    ALOGV("%s: Start camera streaming ops, package name = %s, client UID = %d",
            __FUNCTION__, String8(mClientPackageName).string(), mClientUid);

    if (mAppOpsManager != nullptr) {
        int32_t mode = mAppOpsManager->startOpNoThrow(AppOpsManager::OP_CAMERA, mClientUid,
                mClientPackageName, /*startIfModeDefault*/ false, mClientFeatureId,
                String16("start camera ") + String16(mCameraIdStr));
        status_t res = handleAppOpMode(mode);
        if (res != OK) {
            return res;
        }
    }

    mOpsStreaming = true;

    return OK;
}

status_t CameraService::BasicClient::finishCameraStreamingOps() {
    ATRACE_CALL();

    if (!mOpsActive) {
        ALOGE("%s: Calling streaming start when not yet active", __FUNCTION__);
        return INVALID_OPERATION;
    }
    if (!mOpsStreaming) {
        ALOGV("%s: Streaming not active!", __FUNCTION__);
        return OK;
    }

    if (mAppOpsManager != nullptr) {
        mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, mClientUid,
                mClientPackageName, mClientFeatureId);
            mOpsActive = false;
        mOpsStreaming = false;
    }

    return OK;
}

status_t CameraService::BasicClient::finishCameraOps() {
    ATRACE_CALL();

    if (mOpsStreaming) {
        // Make sure we've notified everyone about camera stopping
        finishCameraStreamingOps();
    }

    // Check if startCameraOps succeeded, and if so, finish the camera op
    if (mOpsActive) {
        mOpsActive = false;

        // This function is called when a client disconnects. This should
        // release the camera, but actually only if it was in a proper
        // functional state, i.e. with status NOT_AVAILABLE
+15 −3
Original line number Diff line number Diff line
@@ -332,9 +332,18 @@ public:
        // - The app-side Binder interface to receive callbacks from us
        sp<IBinder>                     mRemoteBinder;   // immutable after constructor

        // permissions management
        // Permissions management methods for camera lifecycle

        // Notify rest of system/apps about camera opening, and check appops
        virtual status_t                startCameraOps();
        // Notify rest of system/apps about camera starting to stream data, and confirm appops
        virtual status_t                startCameraStreamingOps();
        // Notify rest of system/apps about camera stopping streaming data
        virtual status_t                finishCameraStreamingOps();
        // Notify rest of system/apps about camera closing
        virtual status_t                finishCameraOps();
        // Handle errors for start/checkOps
        virtual status_t                handleAppOpMode(int32_t mode);

        std::unique_ptr<AppOpsManager>  mAppOpsManager = nullptr;

@@ -349,9 +358,12 @@ public:
        }; // class OpsCallback

        sp<OpsCallback> mOpsCallback;
        // Track whether startCameraOps was called successfully, to avoid
        // finishing what we didn't start.
        // Track whether checkOps was called successfully, to avoid
        // finishing what we didn't start, on camera open.
        bool            mOpsActive;
        // Track whether startOps was called successfully on start of
        // camera streaming.
        bool            mOpsStreaming;

        // IAppOpsCallback interface, indirected through opListener
        virtual void opChanged(int32_t op, const String16& packageName);
+5 −0
Original line number Diff line number Diff line
@@ -275,12 +275,17 @@ void CameraOfflineSessionClient::notifyShutter(const CaptureResultExtras& result
    }
}

status_t CameraOfflineSessionClient::notifyActive() {
    return startCameraStreamingOps();
}

void CameraOfflineSessionClient::notifyIdle(
        int64_t /*requestCount*/, int64_t /*resultErrorCount*/, bool /*deviceError*/,
        const std::vector<hardware::CameraStreamStats>& /*streamStats*/) {
    if (mRemoteCallback.get() != nullptr) {
        mRemoteCallback->onDeviceIdle();
    }
    finishCameraStreamingOps();
}

void CameraOfflineSessionClient::notifyAutoFocus(uint8_t newState, int triggerId) {
+1 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ public:
    // NotificationListener API
    void notifyError(int32_t errorCode, const CaptureResultExtras& resultExtras) override;
    void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) override;
    status_t notifyActive() override;
    void notifyIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
            const std::vector<hardware::CameraStreamStats>& streamStats) override;
    void notifyAutoFocus(uint8_t newState, int triggerId) override;
+22 −5
Original line number Diff line number Diff line
@@ -249,11 +249,33 @@ void Camera2ClientBase<TClientBase>::notifyError(
          resultExtras.requestId);
}

template <typename TClientBase>
status_t Camera2ClientBase<TClientBase>::notifyActive() {
    if (!mDeviceActive) {
        status_t res = TClientBase::startCameraStreamingOps();
        if (res != OK) {
            ALOGE("%s: Camera %s: Error starting camera streaming ops: %d", __FUNCTION__,
                    TClientBase::mCameraIdStr.string(), res);
            return res;
        }
        CameraServiceProxyWrapper::logActive(TClientBase::mCameraIdStr);
    }
    mDeviceActive = true;

    ALOGV("Camera device is now active");
    return OK;
}

template <typename TClientBase>
void Camera2ClientBase<TClientBase>::notifyIdle(
        int64_t requestCount, int64_t resultErrorCount, bool deviceError,
        const std::vector<hardware::CameraStreamStats>& streamStats) {
    if (mDeviceActive) {
        status_t res = TClientBase::finishCameraStreamingOps();
        if (res != OK) {
            ALOGE("%s: Camera %s: Error finishing streaming ops: %d", __FUNCTION__,
                    TClientBase::mCameraIdStr.string(), res);
        }
        CameraServiceProxyWrapper::logIdle(TClientBase::mCameraIdStr,
                requestCount, resultErrorCount, deviceError, streamStats);
    }
@@ -268,11 +290,6 @@ void Camera2ClientBase<TClientBase>::notifyShutter(const CaptureResultExtras& re
    (void)resultExtras;
    (void)timestamp;

    if (!mDeviceActive) {
        CameraServiceProxyWrapper::logActive(TClientBase::mCameraIdStr);
    }
    mDeviceActive = true;

    ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64,
            __FUNCTION__, resultExtras.requestId, timestamp);
}
Loading