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

Commit a0c61f11 authored by Austin Borger's avatar Austin Borger
Browse files

Use data delivery permission checks

The new PermissionChecker APIs check permission for a client while also
intiating an AppOp at the same time. They are also capable of checking
permissions for the full AttributionSource chain in the process.

In this change, we switch out the legacy AppOpsManager APIs for the
new PermissionChecker APIs. Some details may need to be ironed out,
particularly when it comes to prioritization of clients. This will be
handled in a future CL.

Bug: 190657833
Bug: 369841571
Test: Ran CameraPermissionTest with 10 iterations for all flag permutations
Flag: com.android.internal.camera.flags.check_full_attribution_source_chain
Change-Id: If6a777bcb9af4f7004a45a2aac35a404be8d9db1
parent 284721c6
Loading
Loading
Loading
Loading
+167 −65
Original line number Diff line number Diff line
@@ -105,6 +105,20 @@ namespace {
        const auto &deviceIdEntry = cameraInfo.find(ANDROID_INFO_DEVICE_ID);
        return deviceIdEntry.data.i32[0];
    }

    static android::PermissionChecker::PermissionResult appOpModeToPermissionResult(int32_t res) {
        switch (res) {
            case android::AppOpsManager::MODE_ERRORED:
                return android::PermissionChecker::PERMISSION_HARD_DENIED;
            case android::AppOpsManager::MODE_IGNORED:
                return android::PermissionChecker::PERMISSION_SOFT_DENIED;
            case android::AppOpsManager::MODE_ALLOWED:
                return android::PermissionChecker::PERMISSION_GRANTED;
        }

        ALOGE("%s: Unexpected appOpMode %d", __FUNCTION__, res);
        return android::PermissionChecker::PERMISSION_HARD_DENIED;
    }
} // namespace anonymous

namespace android {
@@ -4073,8 +4087,8 @@ CameraService::BasicClient::BasicClient(
      mRotationOverride(rotationOverride),
      mAudioRestriction(hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE),
      mRemoteBinder(remoteCallback),
      mOpsActive(false),
      mOpsStreaming(false) {
      mCameraOpen(false),
      mCameraStreaming(false) {
    if (sCameraService == nullptr) {
        sCameraService = cameraService;
    }
@@ -4119,7 +4133,7 @@ binder::Status CameraService::BasicClient::disconnect() {
        remote->unlinkToDeath(sCameraService);
    }

    finishCameraOps();
    notifyCameraClosing();
    // Notify flashlight that a camera device is closed.
    sCameraService->mFlashlight->deviceClosed(mCameraIdStr);
    ALOGI("%s: Disconnected client for camera %s for PID %d", __FUNCTION__, mCameraIdStr.c_str(),
@@ -4219,14 +4233,15 @@ bool CameraService::BasicClient::isValidAudioRestriction(int32_t mode) {
    }
}

status_t CameraService::BasicClient::handleAppOpMode(int32_t mode) {
    if (mode == AppOpsManager::MODE_ERRORED) {
status_t CameraService::BasicClient::handlePermissionResult(
        PermissionChecker::PermissionResult result) {
    if (result == PermissionChecker::PERMISSION_HARD_DENIED) {
        ALOGI("Camera %s: Access for \"%s\" has been revoked", mCameraIdStr.c_str(),
              getPackageName().c_str());
        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.
    } else if (!mUidIsTrusted && result == PermissionChecker::PERMISSION_SOFT_DENIED) {
        // If the calling Uid is trusted (a native service), the AppOpsManager/PermissionChecker
        // could return MODE_IGNORED/PERMISSION_SOFT_DENIED. Do not treat such case as error.
        bool isUidActive =
                sCameraService->mUidPolicy->isUidActive(getClientUid(), getPackageName());

@@ -4255,21 +4270,27 @@ status_t CameraService::BasicClient::handleAppOpMode(int32_t mode) {
    return OK;
}

status_t CameraService::BasicClient::startCameraOps() {
status_t CameraService::BasicClient::handleAppOpMode(int32_t mode) {
    return handlePermissionResult(appOpModeToPermissionResult(mode));
}

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

    {
        ALOGV("%s: Start camera ops, package name = %s, client UID = %d", __FUNCTION__,
    // Don't start watching until we're streaming when using permissionChecker for data delivery
    if (!flags::check_full_attribution_source_chain()) {
        ALOGD("%s: Start camera ops, package name = %s, client UID = %d", __FUNCTION__,
              getPackageName().c_str(), getClientUid());
    }

        if (mAppOpsManager != nullptr) {
            // Notify app ops that the camera is not available
            mOpsCallback = new OpsCallback(this);

        mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA, toString16(getPackageName()),
            mAppOpsManager->startWatchingMode(
                    AppOpsManager::OP_CAMERA, toString16(getPackageName()),
                    AppOpsManager::WATCH_FOREGROUND_CHANGES, mOpsCallback);

        // Just check for camera acccess here on open - delay startOp until
            // Just check for camera access here on open - delay startOp until
            // camera frames start streaming in startCameraStreamingOps
            int32_t mode = mAppOpsManager->checkOp(AppOpsManager::OP_CAMERA, getClientUid(),
                                                   toString16(getPackageName()));
@@ -4278,8 +4299,12 @@ status_t CameraService::BasicClient::startCameraOps() {
                return res;
            }
        }
    } else {
        // TODO: Remove when removing the check_full_attribution_source_chain flag
        ALOGD("%s: Bypassing checkOp for uid %d", __FUNCTION__, getClientUid());
    }

    mOpsActive = true;
    mCameraOpen = true;

    // Transition device availability listeners from PRESENT -> NOT_AVAILABLE
    sCameraService->updateStatus(StatusInternal::NOT_AVAILABLE, mCameraIdStr);
@@ -4295,11 +4320,12 @@ status_t CameraService::BasicClient::startCameraOps() {
status_t CameraService::BasicClient::startCameraStreamingOps() {
    ATRACE_CALL();

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

    if (mCameraStreaming) {
        ALOGV("%s: Streaming already active!", __FUNCTION__);
        return OK;
    }
@@ -4308,6 +4334,26 @@ status_t CameraService::BasicClient::startCameraStreamingOps() {
          getPackageName().c_str(), getClientUid());

    if (mAppOpsManager != nullptr) {
        if (flags::check_full_attribution_source_chain()) {
            ALOGD("%s: Start data delivery for uid %d", __FUNCTION__, getClientUid());

            const PermissionChecker::PermissionResult result =
                    checkPermissionsForCameraForStartDataDelivery(mCameraIdStr, mClientAttribution);
            status_t res = handlePermissionResult(result);
            if (res != OK) {
                return res;
            }

            mOpsCallback = new OpsCallback(this);
            std::for_each(AttrSourceItr{mClientAttribution}, AttrSourceItr::end(),
                      [&](const auto& attr) {
                          mAppOpsManager->startWatchingMode(
                                  AppOpsManager::OP_CAMERA,
                                  toString16(attr.packageName.value_or("")),
                                  AppOpsManager::WATCH_FOREGROUND_CHANGES, mOpsCallback);
                      });
        } else {
            ALOGD("%s: startOp for uid %d", __FUNCTION__, getClientUid());
            int32_t mode = mAppOpsManager->startOpNoThrow(
                    AppOpsManager::OP_CAMERA, getClientUid(), toString16(getPackageName()),
                    /*startIfModeDefault*/ false, toString16(getClientAttributionTag()),
@@ -4317,8 +4363,9 @@ status_t CameraService::BasicClient::startCameraStreamingOps() {
                return res;
            }
        }
    }

    mOpsStreaming = true;
    mCameraStreaming = true;

    return OK;
}
@@ -4331,7 +4378,12 @@ status_t CameraService::BasicClient::noteAppOp() {

    // noteAppOp is only used for when camera mute is not supported, in order
    // to trigger the sensor privacy "Unblock" dialog
    if (mAppOpsManager != nullptr) {
    if (flags::check_full_attribution_source_chain()) {
        // Ignore the result, since we're only triggering the dialog
        ALOGD("%s: Check data delivery permissions for uid %d", __FUNCTION__, getClientUid());
        hasPermissionsForCameraForDataDelivery(std::string(), mClientAttribution);
    } else if (mAppOpsManager != nullptr) {
        ALOGD("%s: noteOp for uid %d", __FUNCTION__, getClientUid());
        int32_t mode = mAppOpsManager->noteOp(
                AppOpsManager::OP_CAMERA, getClientUid(), toString16(getPackageName()),
                toString16(getClientAttributionTag()),
@@ -4348,36 +4400,48 @@ status_t CameraService::BasicClient::noteAppOp() {
status_t CameraService::BasicClient::finishCameraStreamingOps() {
    ATRACE_CALL();

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

    if (mAppOpsManager != nullptr) {
        if (flags::check_full_attribution_source_chain()) {
            ALOGD("%s: finishDataDelivery for uid %d", __FUNCTION__, getClientUid());
            finishDataDelivery(mClientAttribution);

            // Stop watching app op changes after stop streaming
            if (mOpsCallback != nullptr) {
                mAppOpsManager->stopWatchingMode(mOpsCallback);
                mOpsCallback.clear();
            }
        } else {
            ALOGD("%s: finishOp for uid %d", __FUNCTION__, getClientUid());
            mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, getClientUid(),
                                     toString16(getPackageName()),
                                     toString16(getClientAttributionTag()));
        mOpsStreaming = false;
        }
        mCameraStreaming = false;
    }

    return OK;
}

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

    if (mOpsStreaming) {
    if (mCameraStreaming) {
        // 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;
    // Check if notifyCameraOpening succeeded, and if so, finish the camera op if necessary
    if (mCameraOpen) {
        mCameraOpen = false;

        // This function is called when a client disconnects. This should
        // release the camera, but actually only if it was in a proper
@@ -4389,11 +4453,15 @@ status_t CameraService::BasicClient::finishCameraOps() {
        sCameraService->updateStatus(StatusInternal::PRESENT,
                mCameraIdStr, rejected);
    }

    // When using the data delivery permission checks, the open state does not involve AppOps
    if (!flags::check_full_attribution_source_chain()) {
        // Always stop watching, even if no camera op is active
        if (mOpsCallback != nullptr && mAppOpsManager != nullptr) {
            mAppOpsManager->stopWatchingMode(mOpsCallback);
        }
        mOpsCallback.clear();
    }

    sCameraService->mUidPolicy->unregisterMonitorUid(getClientUid(), /*closeCamera*/ true);

@@ -4414,26 +4482,60 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16&) {
        return;
    }

    int32_t res;
    res = mAppOpsManager->checkOp(AppOpsManager::OP_CAMERA, getClientUid(),
    PermissionChecker::PermissionResult res;
    if (flags::check_full_attribution_source_chain()) {
        int32_t appOpMode = AppOpsManager::MODE_ALLOWED;
        std::for_each(AttrSourceItr{mClientAttribution}, AttrSourceItr::end(),
                [&](const auto& attr) {
                    appOpMode = std::max(appOpMode, mAppOpsManager->checkOp(
                            AppOpsManager::OP_CAMERA, attr.uid,
                            toString16(attr.packageName.value_or(""))));
                });
        ALOGV("checkOp returns: %d, %s ", res,
              appOpMode == AppOpsManager::MODE_ALLOWED   ? "ALLOWED"
              : appOpMode == AppOpsManager::MODE_IGNORED ? "IGNORED"
              : appOpMode == AppOpsManager::MODE_ERRORED ? "ERRORED"
                                                         : "UNKNOWN");
        res = appOpModeToPermissionResult(appOpMode);
    } else {
        int32_t appOpMode = mAppOpsManager->checkOp(AppOpsManager::OP_CAMERA, getClientUid(),
                                                    toString16(getPackageName()));
        ALOGV("checkOp returns: %d, %s ", res,
            res == AppOpsManager::MODE_ALLOWED ? "ALLOWED" :
            res == AppOpsManager::MODE_IGNORED ? "IGNORED" :
            res == AppOpsManager::MODE_ERRORED ? "ERRORED" :
            "UNKNOWN");
              appOpMode == AppOpsManager::MODE_ALLOWED   ? "ALLOWED"
              : appOpMode == AppOpsManager::MODE_IGNORED ? "IGNORED"
              : appOpMode == AppOpsManager::MODE_ERRORED ? "ERRORED"
                                                         : "UNKNOWN");
        res = appOpModeToPermissionResult(appOpMode);
    }

    if (res == AppOpsManager::MODE_ERRORED) {
    if (res == PermissionChecker::PERMISSION_HARD_DENIED) {
        ALOGI("Camera %s: Access for \"%s\" revoked", mCameraIdStr.c_str(),
              getPackageName().c_str());
        block();
    } else if (res == AppOpsManager::MODE_IGNORED) {
    } else if (res == PermissionChecker::PERMISSION_SOFT_DENIED) {
        bool isUidActive =
                sCameraService->mUidPolicy->isUidActive(getClientUid(), getPackageName());

        // Uid may be active, but not visible to the user (e.g. PROCESS_STATE_FOREGROUND_SERVICE).
        // If not visible, but still active, then we want to block instead of muting the camera.
        int32_t procState = sCameraService->mUidPolicy->getProcState(getClientUid());
        int32_t procState = ActivityManager::PROCESS_STATE_NONEXISTENT;
        if (flags::check_full_attribution_source_chain()) {
            // Use the proc state of the last uid in the chain (ultimately receiving the data)
            // when determining whether to mute or block
            int32_t uid = -1;
            std::for_each(AttrSourceItr{mClientAttribution}, AttrSourceItr::end(),
                      [&](const auto& attr) {
                          uid = static_cast<uid_t>(attr.uid);
                      });
            const auto& activityManager = getActivityManager();
            if (activityManager != nullptr) {
                procState = activityManager->getUidProcessState(uid, toString16(kServiceName));
            } else {
                ALOGD("%s: getActivityManager returned nullptr.", __FUNCTION__);
            }
        } else {
            procState = sCameraService->mUidPolicy->getProcState(getClientUid());
        }
        bool isUidVisible = (procState <= ActivityManager::PROCESS_STATE_BOUND_TOP);

        bool isCameraPrivacyEnabled;
@@ -4446,9 +4548,9 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16&) {
        }

        ALOGI("Camera %s: Access for \"%s\" has been restricted, isUidTrusted %d, isUidActive %d"
              " isUidVisible %d, isCameraPrivacyEnabled %d",
              " isUidVisible %d, isCameraPrivacyEnabled %d procState %d",
              mCameraIdStr.c_str(), getPackageName().c_str(), mUidIsTrusted, isUidActive,
              isUidVisible, isCameraPrivacyEnabled);
              isUidVisible, isCameraPrivacyEnabled, procState);
        // If the calling Uid is trusted (a native service), or the client Uid is active / visible
        // (WAR for b/175320666)the AppOpsManager could return MODE_IGNORED. Do not treat such
        // cases as error.
@@ -4459,7 +4561,7 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16&) {
                block();
            }
        }
    } else if (res == AppOpsManager::MODE_ALLOWED) {
    } else if (res == PermissionChecker::PERMISSION_GRANTED) {
        setCameraMute(sCameraService->mOverrideCameraMuteMode);
    }
}
+12 −12
Original line number Diff line number Diff line
@@ -462,13 +462,13 @@ public:

        virtual ~BasicClient();

        // the instance is in the middle of destruction. When this is set,
        // The instance is in the middle of destruction. When this is set,
        // the instance should not be accessed from callback.
        // CameraService's mClientLock should be acquired to access this.
        // - subclasses should set this to true in their destructors.
        bool mDestructionStarted;

        // these are initialized in the constructor.
        // These are initialized in the constructor.
        static sp<CameraService>        sCameraService;
        const std::string               mCameraIdStr;
        const int                       mCameraFacing;
@@ -489,16 +489,18 @@ public:

        // 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 opening, and (legacy) check appops
        virtual status_t                notifyCameraOpening();
        // 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                notifyCameraClosing();
        // Handle errors for start/checkOps, startDataDelivery
        virtual status_t                handleAppOpMode(int32_t mode);
        virtual status_t                handlePermissionResult(
                                                PermissionChecker::PermissionResult result);
        // Just notify camera appops to trigger unblocking dialog if sensor
        // privacy is enabled and camera mute is not supported
        virtual status_t                noteAppOp();
@@ -516,12 +518,10 @@ public:
        }; // class OpsCallback

        sp<OpsCallback> mOpsCallback;
        // 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;
        // Track if the camera is currently active.
        bool mCameraOpen;
        // Track if the camera is currently streaming.
        bool mCameraStreaming;

        // IAppOpsCallback interface, indirected through opListener
        virtual void opChanged(int32_t op, const String16& packageName);
+10 −10
Original line number Diff line number Diff line
@@ -34,8 +34,8 @@ status_t CameraOfflineSessionClient::initialize(sp<CameraProviderManager>, const
        return OK;
    }

    // Verify ops permissions
    auto res = startCameraOps();
    // Verify ops permissions and/or open camera
    auto res = notifyCameraOpening();
    if (res != OK) {
        return res;
    }
@@ -184,7 +184,7 @@ binder::Status CameraOfflineSessionClient::disconnect() {
    mFrameProcessor->requestExit();
    mFrameProcessor->join();

    finishCameraOps();
    notifyCameraClosing();
    ALOGI("%s: Disconnected client for offline camera %s for PID %d", __FUNCTION__,
            mCameraIdStr.c_str(), mCallingPid);

@@ -227,10 +227,10 @@ void CameraOfflineSessionClient::notifyError(int32_t errorCode,
    }
}

status_t CameraOfflineSessionClient::startCameraOps() {
status_t CameraOfflineSessionClient::notifyCameraOpening() {
    ATRACE_CALL();
    {
        ALOGV("%s: Start camera ops, package name = %s, client UID = %d", __FUNCTION__,
        ALOGV("%s: Notify camera opening, package name = %s, client UID = %d", __FUNCTION__,
              getPackageName().c_str(), getClientUid());
    }

@@ -262,7 +262,7 @@ status_t CameraOfflineSessionClient::startCameraOps() {
        }
    }

    mOpsActive = true;
    mCameraOpen = true;

    // Transition device state to OPEN
    sCameraService->mUidPolicy->registerMonitorUid(getClientUid(), /*openCamera*/ true);
@@ -270,17 +270,17 @@ status_t CameraOfflineSessionClient::startCameraOps() {
    return OK;
}

status_t CameraOfflineSessionClient::finishCameraOps() {
status_t CameraOfflineSessionClient::notifyCameraClosing() {
    ATRACE_CALL();

    // Check if startCameraOps succeeded, and if so, finish the camera op
    if (mOpsActive) {
    // Check if notifyCameraOpening succeeded, and if so, finish the camera op if necessary
    if (mCameraOpen) {
        // Notify app ops that the camera is available again
        if (mAppOpsManager != nullptr) {
            // TODO : possibly change this to OP_OFFLINE_CAMERA_SESSION
            mAppOpsManager->finishOp(AppOpsManager::OP_CAMERA, getClientUid(),
                                     toString16(getPackageName()));
            mOpsActive = false;
            mCameraOpen = false;
        }
    }
    // Always stop watching, even if no camera op is active
+2 −2
Original line number Diff line number Diff line
@@ -98,8 +98,8 @@ public:
    status_t setZoomOverride(int32_t zoomOverride) override;

    // permissions management
    status_t startCameraOps() override;
    status_t finishCameraOps() override;
    status_t notifyCameraOpening() override;
    status_t notifyCameraClosing() override;

    // FilteredResultListener API
    void onResultAvailable(const CaptureResult& result) override;
+4 −4
Original line number Diff line number Diff line
@@ -131,10 +131,10 @@ status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr
        return NO_INIT;
    }

    // Verify ops permissions
    res = TClientBase::startCameraOps();
    // Notify camera opening (check op if check_full_attribution_source_chain flag is off).
    res = TClientBase::notifyCameraOpening();
    if (res != OK) {
        TClientBase::finishCameraOps();
        TClientBase::notifyCameraClosing();
        return res;
    }

@@ -142,7 +142,7 @@ status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr
    if (res != OK) {
        ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
                __FUNCTION__, TClientBase::mCameraIdStr.c_str(), strerror(-res), res);
        TClientBase::finishCameraOps();
        TClientBase::notifyCameraClosing();
        return res;
    }

Loading