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

Commit 253930b9 authored by Ruben Brunk's avatar Ruben Brunk Committed by Android (Google) Code Review
Browse files

Merge "camera2: Enforce client eviction on user switch."

parents 41919bfa 36597b24
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -302,6 +302,15 @@ public:
        status_t res = data.readInt32();
        return res;
    }

    virtual void notifySystemEvent(int eventId, int arg0) {
        Parcel data, reply;
        data.writeInt32(eventId);
        data.writeInt32(arg0);
        remote()->transact(BnCameraService::NOTIFY_SYSTEM_EVENT, data, &reply,
                IBinder::FLAG_ONEWAY);
    }

};

IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -470,6 +479,13 @@ status_t BnCameraService::onTransact(
            reply->writeInt32(status);
            return NO_ERROR;
        } break;
        case NOTIFY_SYSTEM_EVENT: {
            CHECK_INTERFACE(ICameraService, data, reply);
            int eventId = data.readInt32();
            int arg0 = data.readInt32();
            notifySystemEvent(eventId, arg0);
            return NO_ERROR;
        } break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
+18 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ public:
        SUPPORTS_CAMERA_API,
        CONNECT_LEGACY,
        SET_TORCH_MODE,
        NOTIFY_SYSTEM_EVENT,
    };

    enum {
@@ -66,6 +67,17 @@ public:
        CAMERA_HAL_API_VERSION_UNSPECIFIED = -1
    };

    /**
     * Keep up-to-date with declarations in
     * frameworks/base/services/core/java/com/android/server/camera/CameraService.java
     *
     * These event codes are intended to be used with the notifySystemEvent call.
     */
    enum {
        NO_EVENT = 0,
        USER_SWITCHED,
    };

public:
    DECLARE_META_INTERFACE(CameraService);

@@ -148,6 +160,11 @@ public:
     */
    virtual status_t setTorchMode(const String16& cameraId, bool enabled,
            const sp<IBinder>& clientBinder) = 0;

    /**
     * Notify the camera service of a system event.  Should only be called from system_server.
     */
    virtual void notifySystemEvent(int eventId, int arg0) = 0;
};

// ----------------------------------------------------------------------------
+110 −12
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@
#include <binder/ProcessInfoService.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <cutils/multiuser.h>
#include <gui/Surface.h>
#include <hardware/hardware.h>
#include <media/AudioSystem.h>
@@ -123,9 +122,8 @@ static void torch_mode_status_change(
// should be ok for now.
static CameraService *gCameraService;

CameraService::CameraService()
    : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), mSoundRef(0), mModule(0), mFlashlight(0)
{
CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH),
        mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) {
    ALOGI("CameraService started (pid=%d)", getpid());
    gCameraService = this;

@@ -757,7 +755,8 @@ status_t CameraService::getLegacyParametersLazy(int cameraId,
    return INVALID_OPERATION;
}

status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& clientUid) const {
status_t CameraService::validateConnectLocked(const String8& cameraId, /*inout*/int& clientUid)
        const {

    int callingPid = getCallingPid();

@@ -797,6 +796,13 @@ status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& c
        return -EACCES;
    }

    // Only allow clients who are being used by the current foreground device user.
    if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) {
        ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground "
                "device user)", callingPid);
        return PERMISSION_DENIED;
    }

    return checkIfDeviceIsUsable(cameraId);
}

@@ -1163,6 +1169,21 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,
    return OK;
}

void CameraService::notifySystemEvent(int eventId, int arg0) {
    switch(eventId) {
        case ICameraService::USER_SWITCHED: {
            doUserSwitch(/*newUserId*/arg0);
            break;
        }
        case ICameraService::NO_EVENT:
        default: {
            ALOGW("%s: Received invalid system event from system_server: %d", __FUNCTION__,
                    eventId);
            break;
        }
    }
}

status_t CameraService::addListener(
                                const sp<ICameraServiceListener>& listener) {
    ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
@@ -1351,6 +1372,8 @@ bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
        // other clients from connecting in mServiceLockWrapper if held
        mServiceLock.unlock();

        // Do not clear caller identity, remote caller should be client proccess

        for (auto& i : evicted) {
            if (i.get() != nullptr) {
                i->disconnect();
@@ -1392,6 +1415,60 @@ sp<CameraService::BasicClient> CameraService::removeClientLocked(const String8&
    return clientDescriptorPtr->getValue();
}

void CameraService::doUserSwitch(int newUserId) {
    // Acquire mServiceLock and prevent other clients from connecting
    std::unique_ptr<AutoConditionLock> lock =
            AutoConditionLock::waitAndAcquire(mServiceLockWrapper);

    if (newUserId <= 0) {
        ALOGW("%s: Bad user ID %d given during user switch, resetting to default.", __FUNCTION__,
                newUserId);
        newUserId = DEFAULT_LAST_USER_ID;
    }

    mLastUserId = newUserId;

    // Current user has switched, evict all current clients.
    std::vector<sp<BasicClient>> evicted;
    for (auto& i : mActiveClientManager.getAll()) {
        auto clientSp = i->getValue();

        if (clientSp.get() == nullptr) {
            ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__);
            continue;
        }

        evicted.push_back(clientSp);

        String8 curTime = getFormattedCurrentTime();

        ALOGE("Evicting conflicting client for camera ID %s due to user change",
                i->getKey().string());
        // Log the clients evicted
        mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
                PRId32 ", priority %" PRId32 ")\n   - Evicted due to user switch.",
                curTime.string(), i->getKey().string(),
                String8{clientSp->getPackageName()}.string(), i->getOwnerId(),
                i->getPriority()));

    }

    // Do not hold mServiceLock while disconnecting clients, but retain the condition
    // blocking other clients from connecting in mServiceLockWrapper if held.
    mServiceLock.unlock();

    // Clear caller identity temporarily so client disconnect PID checks work correctly
    int64_t token = IPCThreadState::self()->clearCallingIdentity();

    for (auto& i : evicted) {
        i->disconnect();
    }

    IPCThreadState::self()->restoreCallingIdentity(token);

    // Reacquire mServiceLock
    mServiceLock.lock();
}

void CameraService::logDisconnected(const String8& cameraId, int clientPid,
        const String8& clientPackage) {
@@ -1411,16 +1488,18 @@ void CameraService::logConnected(const String8& cameraId, int clientPid,
            curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
}

status_t CameraService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
        uint32_t flags) {

    const int pid = getCallingPid();
    const int selfPid = getpid();

    // Permission checks
    switch (code) {
        case BnCameraService::CONNECT:
        case BnCameraService::CONNECT_DEVICE:
        case BnCameraService::CONNECT_LEGACY:
            const int pid = getCallingPid();
            const int self_pid = getpid();
            if (pid != self_pid) {
        case BnCameraService::CONNECT_LEGACY: {
            if (pid != selfPid) {
                // we're called from a different process, do the real check
                if (!checkCallingPermission(
                        String16("android.permission.CAMERA"))) {
@@ -1432,6 +1511,21 @@ status_t CameraService::onTransact(
            }
            break;
        }
        case BnCameraService::NOTIFY_SYSTEM_EVENT: {
            if (pid != selfPid) {
                // Ensure we're being called by system_server, or similar process with
                // permissions to notify the camera service about system events
                if (!checkCallingPermission(
                        String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) {
                    const int uid = getCallingUid();
                    ALOGE("Permission Denial: cannot send updates to camera service about system"
                            " events from pid=%d, uid=%d", pid, uid);
                    return PERMISSION_DENIED;
                }
            }
            break;
        }
    }

    return BnCameraService::onTransact(code, data, reply, flags);
}
@@ -1544,7 +1638,11 @@ CameraService::BasicClient::~BasicClient() {
}

void CameraService::BasicClient::disconnect() {
    if (mDisconnected) return;
    if (mDisconnected) {
        ALOGE("%s: Disconnect called on already disconnected client for device %d", __FUNCTION__,
                mCameraId);
        return;
    }
    mDisconnected = true;;

    mCameraService->removeByClient(this);
+24 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H

#include <cutils/multiuser.h>
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <binder/AppOpsManager.h>
@@ -92,6 +93,11 @@ public:
    // Default number of messages to store in eviction log
    static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;

    enum {
        // Default last user id
        DEFAULT_LAST_USER_ID = 0,
    };

    // Implementation of BinderService<T>
    static char const* getServiceName() { return "media.camera"; }

@@ -145,6 +151,8 @@ public:
    virtual status_t    setTorchMode(const String16& cameraId, bool enabled,
            const sp<IBinder>& clientBinder);

    virtual void notifySystemEvent(int eventId, int arg0);

    // OK = supports api of that version, -EOPNOTSUPP = does not support
    virtual status_t    supportsCameraApi(
            int cameraId, int apiVersion);
@@ -447,7 +455,7 @@ private:
    virtual void onFirstRef();

    // Check if we can connect, before we acquire the service lock.
    status_t validateConnect(const String8& cameraId, /*inout*/int& clientUid) const;
    status_t validateConnectLocked(const String8& cameraId, /*inout*/int& clientUid) const;

    // Handle active client evictions, and update service state.
    // Only call with with mServiceLock held.
@@ -485,6 +493,9 @@ private:
    // Circular buffer for storing event logging for dumps
    RingBuffer<String8> mEventLog;

    // UID of last user.
    int mLastUserId;

    /**
     * Get the camera state for a given camera id.
     *
@@ -529,6 +540,11 @@ private:
     */
    sp<CameraService::BasicClient> removeClientLocked(const String8& cameraId);

    /**
     * Handle a notification that the current device user has changed.
     */
    void doUserSwitch(int newUserId);

    /**
     * Add a event log message that a client has been disconnected.
     */
@@ -702,22 +718,24 @@ status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String
            "Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
            halVersion, static_cast<int>(effectiveApiLevel));

    // Enforce client permissions and do basic sanity checks
    if((ret = validateConnect(cameraId, /*inout*/clientUid)) != NO_ERROR) {
        return ret;
    }

    sp<CLIENT> client = nullptr;
    {
        // Acquire mServiceLock and prevent other clients from connecting
        std::unique_ptr<AutoConditionLock> lock =
                AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);

        if (lock == nullptr) {
            ALOGE("CameraService::connect X (PID %d) rejected (too many other clients connecting)."
                    , clientPid);
            return -EBUSY;
        }

        // Enforce client permissions and do basic sanity checks
        if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
            return ret;
        }
        mLastUserId = multiuser_get_user_id(clientUid);

        // Check the shim parameters after acquiring lock, if they have already been updated and
        // we were doing a shim update, return immediately
        if (shimUpdateOnly) {