diff options
| author | Ruben Brunk <rubenbrunk@google.com> | 2015-02-17 20:18:47 -0800 | 
|---|---|---|
| committer | Ruben Brunk <rubenbrunk@google.com> | 2015-03-18 22:21:21 -0700 | 
| commit | cc776718c0be7c31fe5ab4fc1446d377be60369f (patch) | |
| tree | c5f0633e27f6c36b938f6a57fea544996ac3d5ae /services/camera | |
| parent | f9d19514b3e620b6339c98a442da833a702915ed (diff) | |
| download | frameworks_av-cc776718c0be7c31fe5ab4fc1446d377be60369f.zip frameworks_av-cc776718c0be7c31fe5ab4fc1446d377be60369f.tar.gz frameworks_av-cc776718c0be7c31fe5ab4fc1446d377be60369f.tar.bz2  | |
camera2: Add camera client eviction enforcement.
- This updates the CameraService to implement client
  eviction behavior based on process priority.
Bug: 19186859
Change-Id: I646939b1cdf1a2237c4e5044164d55a2542cf36e
Diffstat (limited to 'services/camera')
12 files changed, 2420 insertions, 941 deletions
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 5d6423a..de841c8 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -54,6 +54,7 @@ LOCAL_SRC_FILES:=               \      device3/StatusTracker.cpp \      gui/RingBufferConsumer.cpp \      utils/CameraTraces.cpp \ +    utils/AutoConditionLock.cpp \  LOCAL_SHARED_LIBRARIES:= \      libui \ diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp index bfef50e..6589e27 100644 --- a/services/camera/libcameraservice/CameraDeviceFactory.cpp +++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp @@ -48,6 +48,7 @@ sp<CameraDeviceBase> CameraDeviceFactory::createDevice(int cameraId) {          case CAMERA_DEVICE_API_VERSION_3_0:          case CAMERA_DEVICE_API_VERSION_3_1:          case CAMERA_DEVICE_API_VERSION_3_2: +        case CAMERA_DEVICE_API_VERSION_3_3:              device = new Camera3Device(cameraId);              break;          default: diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp index 8e894cd..6c237aa 100644 --- a/services/camera/libcameraservice/CameraFlashlight.cpp +++ b/services/camera/libcameraservice/CameraFlashlight.cpp @@ -110,6 +110,20 @@ status_t CameraFlashlight::setTorchMode(const String8& cameraId, bool enabled) {      status_t res = OK;      Mutex::Autolock l(mLock); +    if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) { +        // This case is needed to avoid state corruption during the following call sequence: +        // CameraService::setTorchMode for camera ID 0 begins, does torch status checks +        // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends +        // CameraService::setTorchMode for camera ID 0 continues, calls +        //        CameraFlashlight::setTorchMode + +        // TODO: Move torch status checks and state updates behind this CameraFlashlight lock +        // to avoid other similar race conditions. +        ALOGE("%s: Camera device %s is in use, cannot set torch mode.", +                __FUNCTION__, cameraId.string()); +        return -EBUSY; +    } +      if (mFlashControl == NULL) {          if (enabled == false) {              return OK; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 6f37f16..574165b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -17,9 +17,14 @@  #define LOG_TAG "CameraService"  //#define LOG_NDEBUG 0 +#include <algorithm> +#include <climits>  #include <stdio.h> -#include <string.h> +#include <cstring> +#include <ctime> +#include <string>  #include <sys/types.h> +#include <inttypes.h>  #include <pthread.h>  #include <binder/AppOpsManager.h> @@ -27,6 +32,7 @@  #include <binder/IServiceManager.h>  #include <binder/MemoryBase.h>  #include <binder/MemoryHeapBase.h> +#include <binder/ProcessInfoService.h>  #include <cutils/atomic.h>  #include <cutils/properties.h>  #include <cutils/multiuser.h> @@ -67,25 +73,16 @@ static void setLogLevel(int level) {  // ---------------------------------------------------------------------------- -static int getCallingPid() { -    return IPCThreadState::self()->getCallingPid(); -} - -static int getCallingUid() { -    return IPCThreadState::self()->getCallingUid(); -} -  extern "C" {  static void camera_device_status_change(          const struct camera_module_callbacks* callbacks,          int camera_id,          int new_status) {      sp<CameraService> cs = const_cast<CameraService*>( -                                static_cast<const CameraService*>(callbacks)); +            static_cast<const CameraService*>(callbacks)); -    cs->onDeviceStatusChanged( -        camera_id, -        new_status); +    cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id), +            static_cast<camera_device_status_t>(new_status));  }  static void torch_mode_status_change( @@ -128,23 +125,20 @@ static void torch_mode_status_change(  static CameraService *gCameraService;  CameraService::CameraService() -    :mSoundRef(0), mModule(0), mFlashlight(0) +    : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), mSoundRef(0), mModule(0), mFlashlight(0)  {      ALOGI("CameraService started (pid=%d)", getpid());      gCameraService = this; -    for (size_t i = 0; i < MAX_CAMERAS; ++i) { -        mStatusList[i] = ICameraServiceListener::STATUS_PRESENT; -    } -      this->camera_device_status_change = android::camera_device_status_change;      this->torch_mode_status_change = android::torch_mode_status_change; +    mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock);  }  void CameraService::onFirstRef()  { -    LOG1("CameraService::onFirstRef"); +    ALOGI("CameraService process starting");      BnCameraService::onFirstRef(); @@ -157,27 +151,53 @@ void CameraService::onFirstRef()      else {          mModule = new CameraModule(rawModule);          const hw_module_t *common = mModule->getRawModule(); -        ALOGI("Loaded \"%s\" cameraCa module", common->name); +        ALOGI("Loaded \"%s\" camera module", common->name);          mNumberOfCameras = mModule->getNumberOfCameras(); -        if (mNumberOfCameras > MAX_CAMERAS) { -            ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).", -                    mNumberOfCameras, MAX_CAMERAS); -            mNumberOfCameras = MAX_CAMERAS; -        }          mFlashlight = new CameraFlashlight(*mModule, *this);          status_t res = mFlashlight->findFlashUnits();          if (res) {              // impossible because we haven't open any camera devices. -            ALOGE("failed to find flash units."); +            ALOGE("Failed to find flash units.");          }          for (int i = 0; i < mNumberOfCameras; i++) { -            setCameraFree(i); +            String8 cameraId = String8::format("%d", i); + +            // Defaults to use for cost and conflicting devices +            int cost = 100; +            char** conflicting_devices = nullptr; +            size_t conflicting_devices_length = 0; + +            // If using post-2.4 module version, query the cost + conflicting devices from the HAL +            if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_4) { +                struct camera_info info; +                status_t rc = mModule->getCameraInfo(i, &info); +                if (rc == NO_ERROR) { +                    cost = info.resource_cost; +                    conflicting_devices = info.conflicting_devices; +                    conflicting_devices_length = info.conflicting_devices_length; +                } else { +                    ALOGE("%s: Received error loading camera info for device %d, cost and" +                            " conflicting devices fields set to defaults for this device.", +                            __FUNCTION__, i); +                } +            } + +            std::set<String8> conflicting; +            for (size_t i = 0; i < conflicting_devices_length; i++) { +                conflicting.emplace(String8(conflicting_devices[i])); +            } + +            // Initialize state for each camera device +            { +                Mutex::Autolock lock(mCameraStatesLock); +                mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost, +                        conflicting)); +            } -            String8 cameraName = String8::format("%d", i); -            if (mFlashlight->hasFlashUnit(cameraName)) { -                mTorchStatusMap.add(cameraName, +            if (mFlashlight->hasFlashUnit(cameraId)) { +                mTorchStatusMap.add(cameraId,                          ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);              }          } @@ -197,80 +217,69 @@ void CameraService::onFirstRef()  }  CameraService::~CameraService() { -    for (int i = 0; i < mNumberOfCameras; i++) { -        if (mBusy[i]) { -            ALOGE("camera %d is still in use in destructor!", i); -        } -    } -      if (mModule) {          delete mModule; +        mModule = nullptr;      }      VendorTagDescriptor::clearGlobalVendorTagDescriptor(); -    gCameraService = NULL; +    gCameraService = nullptr;  } -void CameraService::onDeviceStatusChanged(int cameraId, -                                          int newStatus) -{ -    ALOGV("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__, +void CameraService::onDeviceStatusChanged(camera_device_status_t  cameraId, +        camera_device_status_t newStatus) { +    ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,            cameraId, newStatus); -    if (cameraId < 0 || cameraId >= MAX_CAMERAS) { +    String8 id = String8::format("%d", cameraId); +    std::shared_ptr<CameraState> state = getCameraState(id); + +    if (state == nullptr) {          ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId);          return;      } -    if ((int)getStatus(cameraId) == newStatus) { -        ALOGE("%s: State transition to the same status 0x%x not allowed", -              __FUNCTION__, (uint32_t)newStatus); +    ICameraServiceListener::Status oldStatus = state->getStatus(); + +    if (oldStatus == static_cast<ICameraServiceListener::Status>(newStatus)) { +        ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus);          return;      } -    /* don't do this in updateStatus -       since it is also called from connect and we could get into a deadlock */      if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) { -        Vector<sp<BasicClient> > clientsToDisconnect; +        sp<BasicClient> clientToDisconnect;          { -           Mutex::Autolock al(mServiceLock); - -           /* Remove cached parameters from shim cache */ -           mShimParams.removeItem(cameraId); - -           /* Find all clients that we need to disconnect */ -           sp<BasicClient> client = mClient[cameraId].promote(); -           if (client.get() != NULL) { -               clientsToDisconnect.push_back(client); -           } - -           int i = cameraId; -           for (size_t j = 0; j < mProClientList[i].size(); ++j) { -               sp<ProClient> cl = mProClientList[i][j].promote(); -               if (cl != NULL) { -                   clientsToDisconnect.push_back(cl); -               } -           } -        } +            // Don't do this in updateStatus to avoid deadlock over mServiceLock +            Mutex::Autolock lock(mServiceLock); -        /* now disconnect them. don't hold the lock -           or we can get into a deadlock */ +            // Set the device status to NOT_PRESENT, clients will no longer be able to connect +            // to this device until the status changes +            updateStatus(ICameraServiceListener::STATUS_NOT_PRESENT, id); -        for (size_t i = 0; i < clientsToDisconnect.size(); ++i) { -            sp<BasicClient> client = clientsToDisconnect[i]; +            // Remove cached shim parameters +            state->setShimParams(CameraParameters()); -            client->disconnect(); -            /** -             * The remote app will no longer be able to call methods on the -             * client since the client PID will be reset to 0 -             */ +            // Remove the client from the list of active clients +            clientToDisconnect = removeClientLocked(id); + +            // Notify the client of disconnection +            clientToDisconnect->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, +                    CaptureResultExtras{});          } -        ALOGV("%s: After unplug, disconnected %zu clients", -              __FUNCTION__, clientsToDisconnect.size()); -    } +        ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL", +                __FUNCTION__, id.string()); -    updateStatus( -            static_cast<ICameraServiceListener::Status>(newStatus), cameraId); +        // Disconnect client +        if (clientToDisconnect.get() != nullptr) { +            // Ensure not in binder RPC so client disconnect PID checks work correctly +            LOG_ALWAYS_FATAL_IF(getCallingPid() != getpid(), +                    "onDeviceStatusChanged must be called from the camera service process!"); +            clientToDisconnect->disconnect(); +        } + +    } else { +        updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id); +    }  } @@ -304,9 +313,11 @@ void CameraService::onTorchStatusChangedLocked(const String8& cameraId,          return;      } -    Vector<sp<ICameraServiceListener> >::const_iterator it; -    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { -        (*it)->onTorchStatusChanged(newStatus, String16(cameraId.string())); +    { +        Mutex::Autolock lock(mStatusListenerLock); +        for (auto& i : mListenerList) { +            i->onTorchStatusChanged(newStatus, String16{cameraId}); +        }      }  } @@ -333,6 +344,15 @@ status_t CameraService::getCameraInfo(int cameraId,      return rc;  } +int CameraService::cameraIdToInt(const String8& cameraId) { +    errno = 0; +    size_t pos = 0; +    int ret = stoi(std::string{cameraId.string()}, &pos); +    if (errno != 0 || pos != cameraId.size()) { +        return -1; +    } +    return ret; +}  status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {      status_t ret = OK; @@ -466,6 +486,54 @@ status_t CameraService::getCameraCharacteristics(int cameraId,      return ret;  } +int CameraService::getCallingPid() { +    return IPCThreadState::self()->getCallingPid(); +} + +int CameraService::getCallingUid() { +    return IPCThreadState::self()->getCallingUid(); +} + +String8 CameraService::getFormattedCurrentTime() { +    time_t now = time(nullptr); +    char formattedTime[64]; +    strftime(formattedTime, sizeof(formattedTime), "%m-%d %H:%M:%S", localtime(&now)); +    return String8(formattedTime); +} + +int CameraService::getCameraPriorityFromProcState(int procState) { +    // Find the priority for the camera usage based on the process state.  Higher priority clients +    // win for evictions. +    // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose +    //       the camera to the top/foreground applications. +    switch(procState) { +        case PROCESS_STATE_TOP: // User visible +            return 100; +        case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground +            return 90; +        case PROCESS_STATE_PERSISTENT: // Persistent system services +        case PROCESS_STATE_PERSISTENT_UI: +            return 80; +        case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes +            return 70; +        case PROCESS_STATE_BACKUP: // Everything else +        case PROCESS_STATE_HEAVY_WEIGHT: +        case PROCESS_STATE_SERVICE: +        case PROCESS_STATE_RECEIVER: +        case PROCESS_STATE_HOME: +        case PROCESS_STATE_LAST_ACTIVITY: +        case PROCESS_STATE_CACHED_ACTIVITY: +        case PROCESS_STATE_CACHED_ACTIVITY_CLIENT: +        case PROCESS_STATE_CACHED_EMPTY: +            return 1; +        case PROCESS_STATE_NONEXISTENT: +            return -1; +        default: +            ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__); +            return -1; +    } +} +  status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {      if (!mModule) {          ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__); @@ -545,54 +613,90 @@ bool CameraService::setUpVendorTags() {      return true;  } -status_t CameraService::initializeShimMetadata(int cameraId) { -    int pid = getCallingPid(); -    int uid = getCallingUid(); -    status_t ret = validateConnect(cameraId, uid); -    if (ret != OK) { -        // Error already logged by callee -        return ret; -    } +status_t CameraService::makeClient(const sp<CameraService>& cameraService, +        const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId, +        int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, +        int halVersion, int deviceVersion, apiLevel effectiveApiLevel, +        /*out*/sp<BasicClient>* client) { -    bool needsNewClient = false; -    sp<Client> client; +    // TODO: Update CameraClients + HAL interface to use strings for Camera IDs +    int id = cameraIdToInt(cameraId); +    if (id == -1) { +        ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__, +                cameraId.string()); +        return BAD_VALUE; +    } -    String16 internalPackageName("media"); -    {   // Scope for service lock -        Mutex::Autolock lock(mServiceLock); -        if (mClient[cameraId] != NULL) { -            client = static_cast<Client*>(mClient[cameraId].promote().get()); -        } -        if (client == NULL) { -            needsNewClient = true; -            ret = connectHelperLocked(/*out*/client, -                                      /*cameraClient*/NULL, // Empty binder callbacks -                                      cameraId, -                                      internalPackageName, -                                      uid, -                                      pid); - -            if (ret != OK) { -                // Error already logged by callee -                return ret; +    if (halVersion < 0 || halVersion == deviceVersion) { +        // Default path: HAL version is unspecified by caller, create CameraClient +        // based on device version reported by the HAL. +        switch(deviceVersion) { +          case CAMERA_DEVICE_API_VERSION_1_0: +            if (effectiveApiLevel == API_1) {  // Camera1 API route +                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); +                *client = new CameraClient(cameraService, tmp, packageName, id, facing, +                        clientPid, clientUid, getpid(), legacyMode); +            } else { // Camera2 API route +                ALOGW("Camera using old HAL version: %d", deviceVersion); +                return -EOPNOTSUPP; +            } +            break; +          case CAMERA_DEVICE_API_VERSION_2_0: +          case CAMERA_DEVICE_API_VERSION_2_1: +          case CAMERA_DEVICE_API_VERSION_3_0: +          case CAMERA_DEVICE_API_VERSION_3_1: +          case CAMERA_DEVICE_API_VERSION_3_2: +          case CAMERA_DEVICE_API_VERSION_3_3: +            if (effectiveApiLevel == API_1) { // Camera1 API route +                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); +                *client = new Camera2Client(cameraService, tmp, packageName, id, facing, +                        clientPid, clientUid, servicePid, legacyMode); +            } else { // Camera2 API route +                sp<ICameraDeviceCallbacks> tmp = +                        static_cast<ICameraDeviceCallbacks*>(cameraCb.get()); +                *client = new CameraDeviceClient(cameraService, tmp, packageName, id, +                        facing, clientPid, clientUid, servicePid);              } +            break; +          default: +            // Should not be reachable +            ALOGE("Unknown camera device HAL version: %d", deviceVersion); +            return INVALID_OPERATION;          } - -        if (client == NULL) { -            ALOGE("%s: Could not connect to client camera device.", __FUNCTION__); -            return BAD_VALUE; +    } else { +        // A particular HAL version is requested by caller. Create CameraClient +        // based on the requested HAL version. +        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && +            halVersion == CAMERA_DEVICE_API_VERSION_1_0) { +            // Only support higher HAL version device opened as HAL1.0 device. +            sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); +            *client = new CameraClient(cameraService, tmp, packageName, id, facing, +                    clientPid, clientUid, servicePid, legacyMode); +        } else { +            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. +            ALOGE("Invalid camera HAL version %x: HAL %x device can only be" +                    " opened as HAL %x device", halVersion, deviceVersion, +                    CAMERA_DEVICE_API_VERSION_1_0); +            return INVALID_OPERATION;          } - -        String8 rawParams = client->getParameters(); -        CameraParameters params(rawParams); -        mShimParams.add(cameraId, params);      } +    return NO_ERROR; +} + +status_t CameraService::initializeShimMetadata(int cameraId) { +    int uid = getCallingUid(); -    // Close client if one was opened solely for this call -    if (needsNewClient) { -        client->disconnect(); +    String16 internalPackageName("media"); +    String8 id = String8::format("%d", cameraId); +    status_t ret = NO_ERROR; +    sp<Client> tmp = nullptr; +    if ((ret = connectHelper<ICameraClient,Client>(sp<ICameraClient>{nullptr}, id, +            static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, API_1, +            false, true, tmp)) != NO_ERROR) { +        ALOGE("%s: Error %d (%s) initializing shim metadata.", __FUNCTION__, ret, strerror(ret)); +        return ret;      } -    return OK; +    return NO_ERROR;  }  status_t CameraService::getLegacyParametersLazy(int cameraId, @@ -608,42 +712,54 @@ status_t CameraService::getLegacyParametersLazy(int cameraId,          return BAD_VALUE;      } -    ssize_t index = -1; -    {   // Scope for service lock -        Mutex::Autolock lock(mServiceLock); -        index = mShimParams.indexOfKey(cameraId); -        // Release service lock so initializeShimMetadata can be called correctly. +    String8 id = String8::format("%d", cameraId); -        if (index >= 0) { -            *parameters = mShimParams[index]; +    // Check if we already have parameters +    { +        // Scope for service lock +        Mutex::Autolock lock(mServiceLock); +        auto cameraState = getCameraState(id); +        if (cameraState == nullptr) { +            ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); +            return BAD_VALUE;          } -    } - -    if (index < 0) { -        int64_t token = IPCThreadState::self()->clearCallingIdentity(); -        ret = initializeShimMetadata(cameraId); -        IPCThreadState::self()->restoreCallingIdentity(token); -        if (ret != OK) { -            // Error already logged by callee -            return ret; +        CameraParameters p = cameraState->getShimParams(); +        if (!p.isEmpty()) { +            *parameters = p; +            return NO_ERROR;          } +    } -        {   // Scope for service lock -            Mutex::Autolock lock(mServiceLock); -            index = mShimParams.indexOfKey(cameraId); - -            LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized"); +    int64_t token = IPCThreadState::self()->clearCallingIdentity(); +    ret = initializeShimMetadata(cameraId); +    IPCThreadState::self()->restoreCallingIdentity(token); +    if (ret != NO_ERROR) { +        // Error already logged by callee +        return ret; +    } -            *parameters = mShimParams[index]; +    // Check for parameters again +    { +        // Scope for service lock +        Mutex::Autolock lock(mServiceLock); +        auto cameraState = getCameraState(id); +        if (cameraState == nullptr) { +            ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); +            return BAD_VALUE; +        } +        CameraParameters p = cameraState->getShimParams(); +        if (!p.isEmpty()) { +            *parameters = p; +            return NO_ERROR;          }      } -    return OK; +    ALOGE("%s: Parameters were not initialized, or were empty.  Device may not be present.", +            __FUNCTION__); +    return INVALID_OPERATION;  } -status_t CameraService::validateConnect(int cameraId, -                                    /*inout*/ -                                    int& clientUid) const { +status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& clientUid) const {      int callingPid = getCallingPid(); @@ -652,23 +768,25 @@ status_t CameraService::validateConnect(int cameraId,      } else {          // We only trust our own process to forward client UIDs          if (callingPid != getpid()) { -            ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)", +            ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",                      callingPid);              return PERMISSION_DENIED;          }      }      if (!mModule) { -        ALOGE("Camera HAL module not loaded"); +        ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)", +                callingPid);          return -ENODEV;      } -    if (cameraId < 0 || cameraId >= mNumberOfCameras) { -        ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", -            callingPid, cameraId); +    if (getCameraState(cameraId) == nullptr) { +        ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, +                cameraId.string());          return -ENODEV;      } +    // Check device policy for this camera      char value[PROPERTY_VALUE_MAX];      char key[PROPERTY_KEY_MAX];      int clientUserId = multiuser_get_user_id(clientUid); @@ -676,142 +794,214 @@ status_t CameraService::validateConnect(int cameraId,      property_get(key, value, "0");      if (strcmp(value, "1") == 0) {          // Camera is disabled by DevicePolicyManager. -        ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); +        ALOGE("CameraService::connect X (PID %d) rejected (camera %s is disabled by device " +                "policy)", callingPid, cameraId.string());          return -EACCES;      } -    ICameraServiceListener::Status currentStatus = getStatus(cameraId); +    return checkIfDeviceIsUsable(cameraId); +} + +status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const { +    auto cameraState = getCameraState(cameraId); +    int callingPid = getCallingPid(); +    if (cameraState == nullptr) { +        ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, +                cameraId.string()); +        return -ENODEV; +    } + +    ICameraServiceListener::Status currentStatus = cameraState->getStatus();      if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) { -        ALOGI("Camera is not plugged in," -               " connect X (pid %d) rejected", callingPid); +        ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)", +                callingPid, cameraId.string());          return -ENODEV;      } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) { -        ALOGI("Camera is enumerating," -               " connect X (pid %d) rejected", callingPid); +        ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)", +                callingPid, cameraId.string());          return -EBUSY;      } -    // Else don't check for STATUS_NOT_AVAILABLE. -    //  -- It's done implicitly in canConnectUnsafe /w the mBusy array -    return OK; +    return NO_ERROR;  } -bool CameraService::canConnectUnsafe(int cameraId, -                                     const String16& clientPackageName, -                                     const sp<IBinder>& remoteCallback, -                                     sp<BasicClient> &client) { -    String8 clientName8(clientPackageName); -    int callingPid = getCallingPid(); +void CameraService::finishConnectLocked(const sp<BasicClient>& client, +        const CameraService::DescriptorPtr& desc) { -    if (mClient[cameraId] != 0) { -        client = mClient[cameraId].promote(); -        if (client != 0) { -            if (remoteCallback == client->getRemote()) { -                LOG1("CameraService::connect X (pid %d) (the same client)", -                     callingPid); -                return true; -            } else { -                // TODOSC: need to support 1 regular client, -                // multiple shared clients here -                ALOGW("CameraService::connect X (pid %d) rejected" -                      " (existing client).", callingPid); -                return false; -            } +    // Make a descriptor for the incoming client +    auto clientDescriptor = CameraService::CameraClientManager::makeClientDescriptor(client, desc); +    auto evicted = mActiveClientManager.addAndEvict(clientDescriptor); + +    logConnected(desc->getKey(), static_cast<int>(desc->getOwnerId()), +            String8(client->getPackageName())); + +    if (evicted.size() > 0) { +        // This should never happen - clients should already have been removed in disconnect +        for (auto& i : evicted) { +            ALOGE("%s: Invalid state: Client for camera %s was not removed in disconnect", +                    __FUNCTION__, i->getKey().string());          } -        mClient[cameraId].clear(); -    } - -    /* -    mBusy is set to false as the last step of the Client destructor, -    after which it is guaranteed that the Client destructor has finished ( -    including any inherited destructors) - -    We only need this for a Client subclasses since we don't allow -    multiple Clents to be opened concurrently, but multiple BasicClient -    would be fine -    */ -    if (mBusy[cameraId]) { -        ALOGW("CameraService::connect X (pid %d, \"%s\") rejected" -                " (camera %d is still busy).", callingPid, -                clientName8.string(), cameraId); -        return false; -    } -    return true; +        LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly", +                __FUNCTION__); +    }  } -status_t CameraService::connectHelperLocked( +status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid, +        apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,          /*out*/ -        sp<Client>& client, -        /*in*/ -        const sp<ICameraClient>& cameraClient, -        int cameraId, -        const String16& clientPackageName, -        int clientUid, -        int callingPid, -        int halVersion, -        bool legacyMode) { +        sp<BasicClient>* client, +        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) { -    // give flashlight a chance to close devices if necessary. -    mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId)); +    status_t ret = NO_ERROR; +    std::vector<sp<BasicClient>> evictedClients; +    DescriptorPtr clientDescriptor; +    { +        if (effectiveApiLevel == API_1) { +            // If we are using API1, any existing client for this camera ID with the same remote +            // should be returned rather than evicted to allow MediaRecorder to work properly. + +            auto current = mActiveClientManager.get(cameraId); +            if (current != nullptr) { +                auto clientSp = current->getValue(); +                if (clientSp.get() != nullptr) { // should never be needed +                    if (clientSp->getRemote() == remoteCallback) { +                        ALOGI("CameraService::connect X (PID %d) (second call from same" +                                "app binder, returning the same client)", clientPid); +                        *client = clientSp; +                        return NO_ERROR; +                    } +                } +            } +        } -    int facing = -1; -    int deviceVersion = getDeviceVersion(cameraId, &facing); +        // Return error if the device was unplugged or removed by the HAL for some reason +        if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { +            return ret; +        } -    if (halVersion < 0 || halVersion == deviceVersion) { -        // Default path: HAL version is unspecified by caller, create CameraClient -        // based on device version reported by the HAL. -        switch(deviceVersion) { -          case CAMERA_DEVICE_API_VERSION_1_0: -            client = new CameraClient(this, cameraClient, -                    clientPackageName, cameraId, -                    facing, callingPid, clientUid, getpid(), legacyMode); -            break; -          case CAMERA_DEVICE_API_VERSION_2_0: -          case CAMERA_DEVICE_API_VERSION_2_1: -          case CAMERA_DEVICE_API_VERSION_3_0: -          case CAMERA_DEVICE_API_VERSION_3_1: -          case CAMERA_DEVICE_API_VERSION_3_2: -            client = new Camera2Client(this, cameraClient, -                    clientPackageName, cameraId, -                    facing, callingPid, clientUid, getpid(), legacyMode); -            break; -          case -1: -            ALOGE("Invalid camera id %d", cameraId); +        // Get current active client PIDs +        std::vector<int> ownerPids(mActiveClientManager.getAllOwners()); +        ownerPids.push_back(clientPid); + +        std::vector<int> priorities(ownerPids.size(), PROCESS_STATE_NONEXISTENT); + +        // Get priorites of all active PIDs +        ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0], +                /*out*/&priorities[0]); + +        // Update all active clients' priorities +        std::map<int,int> pidToPriorityMap; +        for (size_t i = 0; i < ownerPids.size() - 1; i++) { +            pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i])); +        } +        mActiveClientManager.updatePriorities(pidToPriorityMap); + +        // Get state for the given cameraId +        auto state = getCameraState(cameraId); +        if (state == nullptr) { +            ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)", +                clientPid, cameraId.string());              return BAD_VALUE; -          default: -            ALOGE("Unknown camera device HAL version: %d", deviceVersion); -            return INVALID_OPERATION;          } -    } else { -        // A particular HAL version is requested by caller. Create CameraClient -        // based on the requested HAL version. -        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && -            halVersion == CAMERA_DEVICE_API_VERSION_1_0) { -            // Only support higher HAL version device opened as HAL1.0 device. -            client = new CameraClient(this, cameraClient, -                    clientPackageName, cameraId, -                    facing, callingPid, clientUid, getpid(), legacyMode); -        } else { -            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. -            ALOGE("Invalid camera HAL version %x: HAL %x device can only be" -                    " opened as HAL %x device", halVersion, deviceVersion, -                    CAMERA_DEVICE_API_VERSION_1_0); -            return INVALID_OPERATION; + +        // Make descriptor for incoming client +        clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId, +                sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()), +                state->getConflicting(), +                getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid); + +        // Find clients that would be evicted +        auto evicted = mActiveClientManager.wouldEvict(clientDescriptor); + +        // If the incoming client was 'evicted,' higher priority clients have the camera in the +        // background, so we cannot do evictions +        if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) { +            ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher" +                    " priority).", clientPid); + +            sp<BasicClient> clientSp = clientDescriptor->getValue(); +            String8 curTime = getFormattedCurrentTime(); +            auto incompatibleClients = +                    mActiveClientManager.getIncompatibleClients(clientDescriptor); + +            String8 msg = String8::format("%s : DENIED connect device %s client for package %s " +                    "(PID %d, priority %d)", curTime.string(), +                    cameraId.string(), packageName.string(), clientPid, +                    getCameraPriorityFromProcState(priorities[priorities.size() - 1])); + +            for (auto& i : incompatibleClients) { +                msg.appendFormat("\n   - Blocked by existing device %s client for package %s" +                        "(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(), +                        String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(), +                        i->getPriority()); +            } + +            // Log the client's attempt +            mEventLog.add(msg); + +            return -EBUSY; +        } + +        for (auto& i : evicted) { +            sp<BasicClient> clientSp = i->getValue(); +            if (clientSp.get() == nullptr) { +                ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__); + +                // TODO: Remove this +                LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list", +                        __FUNCTION__); +                mActiveClientManager.remove(i); +                continue; +            } + +            ALOGE("CameraService::connect evicting conflicting client for camera ID %s", +                    i->getKey().string()); +            evictedClients.push_back(clientSp); + +            String8 curTime = getFormattedCurrentTime(); + +            // Log the clients evicted +            mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" +                    PRId32 ", priority %" PRId32 ")\n   - Evicted by device %s client for " +                    "package %s (PID %d, priority %" PRId32 ")", curTime.string(), +                    i->getKey().string(), String8{clientSp->getPackageName()}.string(), +                    i->getOwnerId(), i->getPriority(), cameraId.string(), +                    packageName.string(), clientPid, +                    getCameraPriorityFromProcState(priorities[priorities.size() - 1]))); + +            // Notify the client of disconnection +            clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, +                    CaptureResultExtras());          }      } -    status_t status = connectFinishUnsafe(client, client->getRemote()); -    if (status != OK) { -        // this is probably not recoverable.. maybe the client can try again -        return status; +    // 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(); + +    // Destroy evicted clients +    for (auto& i : evictedClients) { +        // Disconnect is blocking, and should only have returned when HAL has cleaned up +        i->disconnect(); // Clients will remove themselves from the active client list here      } +    evictedClients.clear(); -    mClient[cameraId] = client; -    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, -         getpid()); +    IPCThreadState::self()->restoreCallingIdentity(token); -    return OK; +    // Once clients have been disconnected, relock +    mServiceLock.lock(); + +    // Check again if the device was unplugged or something while we weren't holding mServiceLock +    if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { +        return ret; +    } + +    *partial = clientDescriptor; +    return NO_ERROR;  }  status_t CameraService::connect( @@ -822,47 +1012,18 @@ status_t CameraService::connect(          /*out*/          sp<ICamera>& device) { -    String8 clientName8(clientPackageName); -    int callingPid = getCallingPid(); - -    LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, -            clientName8.string(), cameraId); - -    status_t status = validateConnect(cameraId, /*inout*/clientUid); -    if (status != OK) { -        return status; -    } - - -    sp<Client> client; -    { -        Mutex::Autolock lock(mServiceLock); -        sp<BasicClient> clientTmp; -        if (!canConnectUnsafe(cameraId, clientPackageName, -                              IInterface::asBinder(cameraClient), -                              /*out*/clientTmp)) { -            return -EBUSY; -        } else if (client.get() != NULL) { -            device = static_cast<Client*>(clientTmp.get()); -            return OK; -        } - -        status = connectHelperLocked(/*out*/client, -                                     cameraClient, -                                     cameraId, -                                     clientPackageName, -                                     clientUid, -                                     callingPid); -        if (status != OK) { -            return status; -        } +    status_t ret = NO_ERROR; +    String8 id = String8::format("%d", cameraId); +    sp<Client> client = nullptr; +    ret = connectHelper<ICameraClient,Client>(cameraClient, id, CAMERA_HAL_API_VERSION_UNSPECIFIED, +            clientPackageName, clientUid, API_1, false, false, /*out*/client); +    if(ret != NO_ERROR) { +        return ret;      } -    // important: release the mutex here so the client can call back -    //    into the service from its destructor (can be at the end of the call)      device = client; -    return OK; +    return NO_ERROR;  }  status_t CameraService::connectLegacy( @@ -887,70 +1048,51 @@ status_t CameraService::connectLegacy(          return INVALID_OPERATION;      } -    String8 clientName8(clientPackageName); -    int callingPid = getCallingPid(); - -    LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid, -            clientName8.string(), cameraId); - -    status_t status = validateConnect(cameraId, /*inout*/clientUid); -    if (status != OK) { -        return status; -    } - -    sp<Client> client; -    { -        Mutex::Autolock lock(mServiceLock); -        sp<BasicClient> clientTmp; -        if (!canConnectUnsafe(cameraId, clientPackageName, -                              IInterface::asBinder(cameraClient), -                              /*out*/clientTmp)) { -            return -EBUSY; -        } else if (client.get() != NULL) { -            device = static_cast<Client*>(clientTmp.get()); -            return OK; -        } - -        status = connectHelperLocked(/*out*/client, -                                     cameraClient, -                                     cameraId, -                                     clientPackageName, -                                     clientUid, -                                     callingPid, -                                     halVersion, -                                     /*legacyMode*/true); -        if (status != OK) { -            return status; -        } +    status_t ret = NO_ERROR; +    String8 id = String8::format("%d", cameraId); +    sp<Client> client = nullptr; +    ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName, +            clientUid, API_1, true, false, /*out*/client); +    if(ret != NO_ERROR) { +        return ret;      } -    // important: release the mutex here so the client can call back -    //    into the service from its destructor (can be at the end of the call)      device = client; -    return OK; +    return NO_ERROR;  } -bool CameraService::validCameraIdForSetTorchMode(const String8& cameraId) { -    // invalid string for int -    if (cameraId.string() == NULL) { -        return false; -    } -    errno = 0; -    char *endptr; -    long id = strtol(cameraId.string(), &endptr, 10); // base 10 -    if (errno || id > INT_MAX || id < INT_MIN || *endptr != 0) { -        return false; -    } +status_t CameraService::connectPro(const sp<IProCameraCallbacks>& cameraCb, +                                   int cameraId, +                                   const String16& clientPackageName, +                                   int clientUid, +                                   /*out*/ +                                   sp<IProCameraUser>& device) { +    ALOGE("%s: Unimplemented, please use connectDevice", __FUNCTION__); +    return INVALID_OPERATION; +} -    // id matches one of the plugged-in devices? -    ICameraServiceListener::Status deviceStatus = getStatus(id); -    if (deviceStatus != ICameraServiceListener::STATUS_PRESENT && -            deviceStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) { -        return false; +status_t CameraService::connectDevice( +        const sp<ICameraDeviceCallbacks>& cameraCb, +        int cameraId, +        const String16& clientPackageName, +        int clientUid, +        /*out*/ +        sp<ICameraDeviceUser>& device) { + +    status_t ret = NO_ERROR; +    String8 id = String8::format("%d", cameraId); +    sp<CameraDeviceClient> client = nullptr; +    ret = connectHelper<ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id, +            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, API_2, false, false, +            /*out*/client); + +    if(ret != NO_ERROR) { +        return ret;      } -    return true; +    device = client; +    return NO_ERROR;  }  status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, @@ -963,7 +1105,15 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,      String8 id = String8(cameraId.string());      // verify id is valid. -    if (validCameraIdForSetTorchMode(id) == false) { +    auto state = getCameraState(id); +    if (state == nullptr) { +        ALOGE("%s: camera id is invalid %s", id.string()); +        return -EINVAL; +    } + +    ICameraServiceListener::Status cameraStatus = state->getStatus(); +    if (cameraStatus != ICameraServiceListener::STATUS_PRESENT && +            cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {          ALOGE("%s: camera id is invalid %s", id.string());          return -EINVAL;      } @@ -979,8 +1129,7 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,          }          if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) { -            if (getStatus(atoi(id.string())) == -                        ICameraServiceListener::STATUS_NOT_AVAILABLE) { +            if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) {                  ALOGE("%s: torch mode of camera %s is not available because "                          "camera is in use", __FUNCTION__, id.string());                  return -EBUSY; @@ -1022,174 +1171,6 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,      return OK;  } -status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client, -                                            const sp<IBinder>& remoteCallback) { -    status_t status = client->initialize(mModule); -    if (status != OK) { -        ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); -        return status; -    } -    if (remoteCallback != NULL) { -        remoteCallback->linkToDeath(this); -    } - -    return OK; -} - -status_t CameraService::connectPro( -                                        const sp<IProCameraCallbacks>& cameraCb, -                                        int cameraId, -                                        const String16& clientPackageName, -                                        int clientUid, -                                        /*out*/ -                                        sp<IProCameraUser>& device) -{ -    if (cameraCb == 0) { -        ALOGE("%s: Callback must not be null", __FUNCTION__); -        return BAD_VALUE; -    } - -    String8 clientName8(clientPackageName); -    int callingPid = getCallingPid(); - -    LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid, -            clientName8.string(), cameraId); -    status_t status = validateConnect(cameraId, /*inout*/clientUid); -    if (status != OK) { -        return status; -    } - -    sp<ProClient> client; -    { -        Mutex::Autolock lock(mServiceLock); -        { -            sp<BasicClient> client; -            if (!canConnectUnsafe(cameraId, clientPackageName, -                                  IInterface::asBinder(cameraCb), -                                  /*out*/client)) { -                return -EBUSY; -            } -        } - -        int facing = -1; -        int deviceVersion = getDeviceVersion(cameraId, &facing); - -        switch(deviceVersion) { -          case CAMERA_DEVICE_API_VERSION_1_0: -            ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", -                  cameraId); -            return -EOPNOTSUPP; -            break; -          case CAMERA_DEVICE_API_VERSION_2_0: -          case CAMERA_DEVICE_API_VERSION_2_1: -          case CAMERA_DEVICE_API_VERSION_3_0: -          case CAMERA_DEVICE_API_VERSION_3_1: -          case CAMERA_DEVICE_API_VERSION_3_2: -            client = new ProCamera2Client(this, cameraCb, clientPackageName, -                    cameraId, facing, callingPid, clientUid, getpid()); -            break; -          case -1: -            ALOGE("Invalid camera id %d", cameraId); -            return BAD_VALUE; -          default: -            ALOGE("Unknown camera device HAL version: %d", deviceVersion); -            return INVALID_OPERATION; -        } - -        status_t status = connectFinishUnsafe(client, client->getRemote()); -        if (status != OK) { -            return status; -        } - -        mProClientList[cameraId].push(client); - -        LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId, -                getpid()); -    } -    // important: release the mutex here so the client can call back -    //    into the service from its destructor (can be at the end of the call) -    device = client; -    return OK; -} - -status_t CameraService::connectDevice( -        const sp<ICameraDeviceCallbacks>& cameraCb, -        int cameraId, -        const String16& clientPackageName, -        int clientUid, -        /*out*/ -        sp<ICameraDeviceUser>& device) -{ - -    String8 clientName8(clientPackageName); -    int callingPid = getCallingPid(); - -    LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid, -            clientName8.string(), cameraId); - -    status_t status = validateConnect(cameraId, /*inout*/clientUid); -    if (status != OK) { -        return status; -    } - -    sp<CameraDeviceClient> client; -    { -        Mutex::Autolock lock(mServiceLock); -        { -            sp<BasicClient> client; -            if (!canConnectUnsafe(cameraId, clientPackageName, -                                  IInterface::asBinder(cameraCb), -                                  /*out*/client)) { -                return -EBUSY; -            } -        } - -        int facing = -1; -        int deviceVersion = getDeviceVersion(cameraId, &facing); - -        // give flashlight a chance to close devices if necessary. -        mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId)); - -        switch(deviceVersion) { -          case CAMERA_DEVICE_API_VERSION_1_0: -            ALOGW("Camera using old HAL version: %d", deviceVersion); -            return -EOPNOTSUPP; -           // TODO: don't allow 2.0  Only allow 2.1 and higher -          case CAMERA_DEVICE_API_VERSION_2_0: -          case CAMERA_DEVICE_API_VERSION_2_1: -          case CAMERA_DEVICE_API_VERSION_3_0: -          case CAMERA_DEVICE_API_VERSION_3_1: -          case CAMERA_DEVICE_API_VERSION_3_2: -            client = new CameraDeviceClient(this, cameraCb, clientPackageName, -                    cameraId, facing, callingPid, clientUid, getpid()); -            break; -          case -1: -            ALOGE("Invalid camera id %d", cameraId); -            return BAD_VALUE; -          default: -            ALOGE("Unknown camera device HAL version: %d", deviceVersion); -            return INVALID_OPERATION; -        } - -        status_t status = connectFinishUnsafe(client, client->getRemote()); -        if (status != OK) { -            // this is probably not recoverable.. maybe the client can try again -            return status; -        } - -        LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId, -                getpid()); - -        mClient[cameraId] = client; -    } -    // important: release the mutex here so the client can call back -    //    into the service from its destructor (can be at the end of the call) - -    device = client; -    return OK; -} - -  status_t CameraService::addListener(                                  const sp<ICameraServiceListener>& listener) {      ALOGV("%s: Add listener %p", __FUNCTION__, listener.get()); @@ -1201,23 +1182,29 @@ status_t CameraService::addListener(      Mutex::Autolock lock(mServiceLock); -    Vector<sp<ICameraServiceListener> >::iterator it, end; -    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { -        if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { -            ALOGW("%s: Tried to add listener %p which was already subscribed", -                  __FUNCTION__, listener.get()); -            return ALREADY_EXISTS; +    { +        Mutex::Autolock lock(mStatusListenerLock); +        for (auto& it : mListenerList) { +            if (IInterface::asBinder(it) == IInterface::asBinder(listener)) { +                ALOGW("%s: Tried to add listener %p which was already subscribed", +                      __FUNCTION__, listener.get()); +                return ALREADY_EXISTS; +            }          } + +        mListenerList.push_back(listener);      } -    mListenerList.push_back(listener);      /* Immediately signal current status to this listener only */      { -        Mutex::Autolock m(mStatusMutex) ; -        int numCams = getNumberOfCameras(); -        for (int i = 0; i < numCams; ++i) { -            listener->onStatusChanged(mStatusList[i], i); +        Mutex::Autolock lock(mCameraStatesLock); +        for (auto& i : mCameraStates) { +            // TODO: Update binder to use String16 for camera IDs and remove; +            int id = cameraIdToInt(i.first); +            if (id == -1) continue; + +            listener->onStatusChanged(i.second->getStatus(), id);          }      } @@ -1228,13 +1215,12 @@ status_t CameraService::addListener(              String16 id = String16(mTorchStatusMap.keyAt(i).string());              listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id);          } -      }      return OK;  } -status_t CameraService::removeListener( -                                const sp<ICameraServiceListener>& listener) { + +status_t CameraService::removeListener(const sp<ICameraServiceListener>& listener) {      ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get());      if (listener == 0) { @@ -1244,11 +1230,13 @@ status_t CameraService::removeListener(      Mutex::Autolock lock(mServiceLock); -    Vector<sp<ICameraServiceListener> >::iterator it; -    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { -        if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { -            mListenerList.erase(it); -            return OK; +    { +        Mutex::Autolock lock(mStatusListenerLock); +        for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { +            if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { +                mListenerList.erase(it); +                return OK; +            }          }      } @@ -1258,10 +1246,7 @@ status_t CameraService::removeListener(      return BAD_VALUE;  } -status_t CameraService::getLegacyParameters( -            int cameraId, -            /*out*/ -            String16* parameters) { +status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) {      ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId);      if (parameters == NULL) { @@ -1316,6 +1301,7 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) {              return OK;          }        case CAMERA_DEVICE_API_VERSION_3_2: +      case CAMERA_DEVICE_API_VERSION_3_3:          ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly",                  __FUNCTION__, cameraId);          return OK; @@ -1330,127 +1316,107 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) {      return OK;  } -void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) { -    int callingPid = getCallingPid(); -    LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid); - -    // Declare this before the lock to make absolutely sure the -    // destructor won't be called with the lock held. +void CameraService::removeByClient(const BasicClient* client) {      Mutex::Autolock lock(mServiceLock); - -    int outIndex; -    sp<BasicClient> client = findClientUnsafe(remoteBinder, outIndex); - -    if (client != 0) { -        // Found our camera, clear and leave. -        LOG1("removeClient: clear camera %d", outIndex); - -        sp<IBinder> remote = client->getRemote(); -        if (remote != NULL) { -            remote->unlinkToDeath(this); -        } - -        mClient[outIndex].clear(); -    } else { - -        sp<ProClient> clientPro = findProClientUnsafe(remoteBinder); - -        if (clientPro != NULL) { -            // Found our camera, clear and leave. -            LOG1("removeClient: clear pro %p", clientPro.get()); - -            IInterface::asBinder(clientPro->getRemoteCallback())->unlinkToDeath(this); +    for (auto& i : mActiveClientManager.getAll()) { +        auto clientSp = i->getValue(); +        if (clientSp.get() == client) { +            mActiveClientManager.remove(i);          }      } - -    LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid);  } -sp<CameraService::ProClient> CameraService::findProClientUnsafe( -                        const wp<IBinder>& cameraCallbacksRemote) -{ -    sp<ProClient> clientPro; - -    for (int i = 0; i < mNumberOfCameras; ++i) { -        Vector<size_t> removeIdx; +bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) { +    const int callingPid = getCallingPid(); +    const int servicePid = getpid(); +    bool ret = false; +    { +        // Acquire mServiceLock and prevent other clients from connecting +        std::unique_ptr<AutoConditionLock> lock = +                AutoConditionLock::waitAndAcquire(mServiceLockWrapper); -        for (size_t j = 0; j < mProClientList[i].size(); ++j) { -            wp<ProClient> cl = mProClientList[i][j]; -            sp<ProClient> clStrong = cl.promote(); -            if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) { -                clientPro = clStrong; -                break; -            } else if (clStrong == NULL) { -                // mark to clean up dead ptr -                removeIdx.push(j); +        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__); +                mActiveClientManager.remove(i); +                continue; +            } +            if (remote == clientSp->getRemote() && (callingPid == servicePid || +                    callingPid == clientSp->getClientPid())) { +                mActiveClientManager.remove(i); +                evicted.push_back(clientSp); + +                // Notify the client of disconnection +                clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, +                        CaptureResultExtras());              }          } -        // remove stale ptrs (in reverse so the indices dont change) -        for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) { -            mProClientList[i].removeAt(removeIdx[j]); -        } - -    } - -    return clientPro; -} +        // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking +        // other clients from connecting in mServiceLockWrapper if held +        mServiceLock.unlock(); -sp<CameraService::BasicClient> CameraService::findClientUnsafe( -                        const wp<IBinder>& cameraClient, int& outIndex) { -    sp<BasicClient> client; +        for (auto& i : evicted) { +            if (i.get() != nullptr) { +                i->disconnect(); +                ret = true; +            } +        } -    for (int i = 0; i < mNumberOfCameras; i++) { +        // Reacquire mServiceLock +        mServiceLock.lock(); -        // This happens when we have already disconnected (or this is -        // just another unused camera). -        if (mClient[i] == 0) continue; +    } // lock is destroyed, allow further connect calls -        // Promote mClient. It can fail if we are called from this path: -        // Client::~Client() -> disconnect() -> removeClientByRemote(). -        client = mClient[i].promote(); +    return ret; +} -        // Clean up stale client entry -        if (client == NULL) { -            mClient[i].clear(); -            continue; -        } -        if (cameraClient == client->getRemote()) { -            // Found our camera -            outIndex = i; -            return client; +std::shared_ptr<CameraService::CameraState> CameraService::getCameraState( +        const String8& cameraId) const { +    std::shared_ptr<CameraState> state; +    { +        Mutex::Autolock lock(mCameraStatesLock); +        auto iter = mCameraStates.find(cameraId); +        if (iter != mCameraStates.end()) { +            state = iter->second;          }      } - -    outIndex = -1; -    return NULL; +    return state;  } -CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) { -    if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; -    return mClient[cameraId].unsafe_get(); -} +sp<CameraService::BasicClient> CameraService::removeClientLocked(const String8& cameraId) { +    // Remove from active clients list +    auto clientDescriptorPtr = mActiveClientManager.remove(cameraId); +    if (clientDescriptorPtr == nullptr) { +        ALOGW("%s: Could not evict client, no client for camera ID %s", __FUNCTION__, +                cameraId.string()); +        return sp<BasicClient>{nullptr}; +    } -Mutex* CameraService::getClientLockById(int cameraId) { -    if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; -    return &mClientLock[cameraId]; +    return clientDescriptorPtr->getValue();  } -sp<CameraService::BasicClient> CameraService::getClientByRemote( -                                const wp<IBinder>& cameraClient) { -    // Declare this before the lock to make absolutely sure the -    // destructor won't be called with the lock held. -    sp<BasicClient> client; +void CameraService::logDisconnected(const String8& cameraId, int clientPid, +        const String8& clientPackage) { -    Mutex::Autolock lock(mServiceLock); +    String8 curTime = getFormattedCurrentTime(); +    // Log the clients evicted +    mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)", +            curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); +} -    int outIndex; -    client = findClientUnsafe(cameraClient, outIndex); +void CameraService::logConnected(const String8& cameraId, int clientPid, +        const String8& clientPackage) { -    return client; +    String8 curTime = getFormattedCurrentTime(); +    // Log the clients evicted +    mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)", +            curTime.string(), cameraId.string(), clientPackage.string(), clientPid));  }  status_t CameraService::onTransact( @@ -1479,24 +1445,6 @@ status_t CameraService::onTransact(      return BnCameraService::onTransact(code, data, reply, flags);  } -// The reason we need this busy bit is a new CameraService::connect() request -// may come in while the previous Client's destructor has not been run or is -// still running. If the last strong reference of the previous Client is gone -// but the destructor has not been finished, we should not allow the new Client -// to be created because we need to wait for the previous Client to tear down -// the hardware first. -void CameraService::setCameraBusy(int cameraId) { -    android_atomic_write(1, &mBusy[cameraId]); - -    ALOGV("setCameraBusy cameraId=%d", cameraId); -} - -void CameraService::setCameraFree(int cameraId) { -    android_atomic_write(0, &mBusy[cameraId]); - -    ALOGV("setCameraFree cameraId=%d", cameraId); -} -  // We share the media players for shutter and recording sound for all clients.  // A reference count is kept to determine when we will actually release the  // media players. @@ -1565,7 +1513,6 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,      mRemoteCallback = cameraClient; -    cameraService->setCameraBusy(cameraId);      cameraService->loadSound();      LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId); @@ -1587,7 +1534,7 @@ CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,          int cameraId, int cameraFacing,          int clientPid, uid_t clientUid,          int servicePid): -        mClientPackageName(clientPackageName) +        mClientPackageName(clientPackageName), mDisconnected(false)  {      mCameraService = cameraService;      mRemoteBinder = remoteCallback; @@ -1606,14 +1553,34 @@ CameraService::BasicClient::~BasicClient() {  }  void CameraService::BasicClient::disconnect() { -    ALOGV("BasicClient::disconnect"); -    mCameraService->removeClientByRemote(mRemoteBinder); +    if (mDisconnected) return; +    mDisconnected = true;; + +    mCameraService->removeByClient(this); +    mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid, +            String8(mClientPackageName)); + +    sp<IBinder> remote = getRemote(); +    if (remote != nullptr) { +        remote->unlinkToDeath(mCameraService); +    }      finishCameraOps(); +    ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid); +      // client shouldn't be able to call into us anymore      mClientPid = 0;  } +String16 CameraService::BasicClient::getPackageName() const { +    return mClientPackageName; +} + + +int CameraService::BasicClient::getClientPid() const { +    return mClientPid; +} +  status_t CameraService::BasicClient::startCameraOps() {      int32_t res;      // Notify app ops that the camera is not available @@ -1639,7 +1606,7 @@ status_t CameraService::BasicClient::startCameraOps() {      // Transition device availability listeners from PRESENT -> NOT_AVAILABLE      mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, -            mCameraId); +            String8::format("%d", mCameraId));      return OK;  } @@ -1652,18 +1619,12 @@ status_t CameraService::BasicClient::finishCameraOps() {                  mClientPackageName);          mOpsActive = false; -        // Notify device availability listeners that this camera is available -        // again - -        StatusVector rejectSourceStates; -        rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT); -        rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING); +        auto rejected = {ICameraServiceListener::STATUS_NOT_PRESENT, +                ICameraServiceListener::STATUS_ENUMERATING}; -        // Transition to PRESENT if the camera is not in either of above 2 -        // states +        // Transition to PRESENT if the camera is not in either of the rejected states          mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, -                mCameraId, -                &rejectSourceStates); +                String8::format("%d", mCameraId), rejected);          // Notify flashlight that a camera device is closed.          mCameraService->mFlashlight->deviceClosed( @@ -1710,26 +1671,15 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa  // ---------------------------------------------------------------------------- -Mutex* CameraService::Client::getClientLockFromCookie(void* user) { -    return gCameraService->getClientLockById((int)(intptr_t) user); -} - -// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should -// be acquired for this to be safe -CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { -    BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user); -    // OK: only CameraClient calls this, and they already cast anyway. -    Client* client = static_cast<Client*>(basicClient); - -    // This could happen if the Client is in the process of shutting down (the -    // last strong reference is gone, but the destructor hasn't finished -    // stopping the hardware). -    if (client == NULL) return NULL; - -    // destruction already started, so should not be accessed -    if (client->mDestructionStarted) return NULL; - -    return client; +// Provide client strong pointer for callbacks. +sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) { +    String8 cameraId = String8::format("%d", (int)(intptr_t) user); +    auto clientDescriptor = gCameraService->mActiveClientManager.get(cameraId); +    if (clientDescriptor != nullptr) { +        return sp<Client>{ +                static_cast<Client*>(clientDescriptor->getValue().get())}; +    } +    return sp<Client>{nullptr};  }  void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, @@ -1741,7 +1691,6 @@ void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode  void CameraService::Client::disconnect() {      ALOGV("Client::disconnect");      BasicClient::disconnect(); -    mCameraService->setCameraFree(mCameraId);  }  CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client): @@ -1784,6 +1733,104 @@ void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCo  }  // ---------------------------------------------------------------------------- +//                  CameraState +// ---------------------------------------------------------------------------- + +CameraService::CameraState::CameraState(const String8& id, int cost, +        const std::set<String8>& conflicting) : mId(id), +        mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {} + +CameraService::CameraState::~CameraState() {} + +ICameraServiceListener::Status CameraService::CameraState::getStatus() const { +    Mutex::Autolock lock(mStatusLock); +    return mStatus; +} + +CameraParameters CameraService::CameraState::getShimParams() const { +    return mShimParams; +} + +void CameraService::CameraState::setShimParams(const CameraParameters& params) { +    mShimParams = params; +} + +int CameraService::CameraState::getCost() const { +    return mCost; +} + +std::set<String8> CameraService::CameraState::getConflicting() const { +    return mConflicting; +} + +String8 CameraService::CameraState::getId() const { +    return mId; +} + +// ---------------------------------------------------------------------------- +//                  CameraClientManager +// ---------------------------------------------------------------------------- + +CameraService::CameraClientManager::~CameraClientManager() {} + +sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient( +        const String8& id) const { +    auto descriptor = get(id); +    if (descriptor == nullptr) { +        return sp<BasicClient>{nullptr}; +    } +    return descriptor->getValue(); +} + +String8 CameraService::CameraClientManager::toString() const { +    auto all = getAll(); +    String8 ret("["); +    bool hasAny = false; +    for (auto& i : all) { +        hasAny = true; +        String8 key = i->getKey(); +        int32_t cost = i->getCost(); +        int32_t pid = i->getOwnerId(); +        int32_t priority = i->getPriority(); +        auto conflicting = i->getConflicting(); +        auto clientSp = i->getValue(); +        String8 packageName; +        if (clientSp.get() != nullptr) { +            packageName = String8{clientSp->getPackageName()}; +        } +        ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %" +                PRId32 ", ", key.string(), cost, pid, priority); + +        if (packageName.size() != 0) { +            ret.appendFormat("Client Package Name: %s", packageName.string()); +        } + +        ret.append(", Conflicting Client Devices: {"); +        for (auto& j : conflicting) { +            ret.appendFormat("%s, ", j.string()); +        } +        ret.append("})"); +    } +    if (hasAny) ret.append("\n"); +    ret.append("]\n"); +    return ret; +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( +        const String8& key, const sp<BasicClient>& value, int32_t cost, +        const std::set<String8>& conflictingKeys, int32_t priority, int32_t ownerId) { + +    return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>( +            key, value, cost, conflictingKeys, priority, ownerId); +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( +        const sp<BasicClient>& value, const CameraService::DescriptorPtr& partial) { +    return makeClientDescriptor(partial->getKey(), value, partial->getCost(), +            partial->getConflicting(), partial->getPriority(), partial->getOwnerId()); +} + +// ----------------------------------------------------------------------------  static const int kDumpLockRetries = 50;  static const int kDumpLockSleep = 60000; @@ -1826,11 +1873,14 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {          }          const hw_module_t* common = mModule->getRawModule(); -        result = String8::format("Camera module HAL API version: 0x%x\n", common->hal_api_version); -        result.appendFormat("Camera module API version: 0x%x\n", common->module_api_version); +        result = String8::format("Camera module HAL API version: %#x\n", common->hal_api_version); +        result.appendFormat("Camera module API version: %#x\n", common->module_api_version);          result.appendFormat("Camera module name: %s\n", common->name);          result.appendFormat("Camera module author: %s\n", common->author); -        result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); +        result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras); +        String8 activeClientString = mActiveClientManager.toString(); +        result.appendFormat("Active Camera Clients:\n%s", activeClientString.string()); +          sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();          if (desc == NULL) { @@ -1845,11 +1895,31 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {              desc->dump(fd, /*verbosity*/2, /*indentation*/4);          } -        for (int i = 0; i < mNumberOfCameras; i++) { -            result = String8::format("Camera %d static information:\n", i); +        result = String8("Prior client events (most recent at top):\n"); + +        for (const auto& msg : mEventLog) { +            result.appendFormat("%s\n", msg.string()); +        } + +        if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) { +            result.append("...\n"); +        } + +        write(fd, result.string(), result.size()); + +        bool stateLocked = tryLock(mCameraStatesLock); +        if (!stateLocked) { +            result = String8::format("CameraStates in use, may be deadlocked\n"); +            write(fd, result.string(), result.size()); +        } + +        for (auto& state : mCameraStates) { +            String8 cameraId = state.first; +            result = String8::format("Camera %s information:\n", cameraId.string());              camera_info info; -            status_t rc = mModule->getCameraInfo(i, &info); +            // TODO: Change getCameraInfo + HAL to use String cameraIds +            status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info);              if (rc != OK) {                  result.appendFormat("  Error reading static information!\n");                  write(fd, result.string(), result.size()); @@ -1863,7 +1933,19 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {                  } else {                      deviceVersion = info.device_version;                  } -                result.appendFormat("  Device version: 0x%x\n", deviceVersion); + +                auto conflicting = state.second->getConflicting(); +                result.appendFormat("  Resource Cost: %d\n", state.second->getCost()); +                result.appendFormat("  Conflicting Devices:"); +                for (auto& id : conflicting) { +                    result.appendFormat(" %s", cameraId.string()); +                } +                if (conflicting.size() == 0) { +                    result.appendFormat(" NONE"); +                } +                result.appendFormat("\n"); + +                result.appendFormat("  Device version: %#x\n", deviceVersion);                  if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) {                      result.appendFormat("  Device static metadata:\n");                      write(fd, result.string(), result.size()); @@ -1872,19 +1954,38 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {                  } else {                      write(fd, result.string(), result.size());                  } + +                CameraParameters p = state.second->getShimParams(); +                if (!p.isEmpty()) { +                    result = String8::format("  Camera1 API shim is using parameters:\n        "); +                    write(fd, result.string(), result.size()); +                    p.dump(fd, args); +                }              } -            sp<BasicClient> client = mClient[i].promote(); -            if (client == 0) { -                result = String8::format("  Device is closed, no client instance\n"); +            auto clientDescriptor = mActiveClientManager.get(cameraId); +            if (clientDescriptor == nullptr) { +                result = String8::format("  Device %s is closed, no client instance\n", +                        cameraId.string());                  write(fd, result.string(), result.size());                  continue;              }              hasClient = true; -            result = String8::format("  Device is open. Client instance dump:\n"); +            result = String8::format("  Device %s is open. Client instance dump:\n\n", +                    cameraId.string()); +            result.appendFormat("Client priority level: %d\n", clientDescriptor->getPriority()); +            result.appendFormat("Client PID: %d\n", clientDescriptor->getOwnerId()); + +            auto client = clientDescriptor->getValue(); +            result.appendFormat("Client package: %s\n", +                    String8(client->getPackageName()).string());              write(fd, result.string(), result.size()); +              client->dump(fd, args);          } + +        if (stateLocked) mCameraStatesLock.unlock(); +          if (!hasClient) {              result = String8::format("\nNo active camera clients yet.\n");              write(fd, result.string(), result.size()); @@ -1908,7 +2009,6 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {                  write(fd, result.string(), result.size());              }          } -      }      return NO_ERROR;  } @@ -1931,124 +2031,68 @@ void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {      }  } -/*virtual*/void CameraService::binderDied( -    const wp<IBinder> &who) { +/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {      /**        * While tempting to promote the wp<IBinder> into a sp,        * it's actually not supported by the binder driver        */ -    ALOGV("java clients' binder died"); -      // check torch client      handleTorchClientBinderDied(who);      // check camera device client -    sp<BasicClient> cameraClient = getClientByRemote(who); - -    if (cameraClient == 0) { -        ALOGV("java clients' binder death already cleaned up (normal case)"); +    if(!evictClientIdByRemote(who)) { +        ALOGV("%s: Java client's binder death already cleaned up (normal case)", __FUNCTION__);          return;      } -    ALOGW("Disconnecting camera client %p since the binder for it " -          "died (this pid %d)", cameraClient.get(), getCallingPid()); - -    cameraClient->disconnect(); - +    ALOGE("%s: Java client's binder died, removing it from the list of active clients", +            __FUNCTION__);  } -void CameraService::updateStatus(ICameraServiceListener::Status status, -                                 int32_t cameraId, -                                 const StatusVector *rejectSourceStates) { -    // do not lock mServiceLock here or can get into a deadlock from -    //  connect() -> ProClient::disconnect -> updateStatus -    Mutex::Autolock lock(mStatusMutex); - -    ICameraServiceListener::Status oldStatus = mStatusList[cameraId]; - -    mStatusList[cameraId] = status; - -    if (oldStatus != status) { -        ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x", -              __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status); +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId) { +    updateStatus(status, cameraId, {}); +} -        if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && -            (status != ICameraServiceListener::STATUS_PRESENT && -             status != ICameraServiceListener::STATUS_ENUMERATING)) { +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId, +        std::initializer_list<ICameraServiceListener::Status> rejectSourceStates) { +    // Do not lock mServiceLock here or can get into a deadlock from +    // connect() -> disconnect -> updateStatus -            ALOGW("%s: From NOT_PRESENT can only transition into PRESENT" -                  " or ENUMERATING", __FUNCTION__); -            mStatusList[cameraId] = oldStatus; -            return; -        } +    auto state = getCameraState(cameraId); -        if (rejectSourceStates != NULL) { -            const StatusVector &rejectList = *rejectSourceStates; -            StatusVector::const_iterator it = rejectList.begin(); - -            /** -             * Sometimes we want to conditionally do a transition. -             * For example if a client disconnects, we want to go to PRESENT -             * only if we weren't already in NOT_PRESENT or ENUMERATING. -             */ -            for (; it != rejectList.end(); ++it) { -                if (oldStatus == *it) { -                    ALOGV("%s: Rejecting status transition for Camera ID %d, " -                          " since the source state was was in one of the bad " -                          " states.", __FUNCTION__, cameraId); -                    mStatusList[cameraId] = oldStatus; -                    return; -                } -            } -        } +    if (state == nullptr) { +        ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__, +                cameraId.string()); +        return; +    } -        /** -          * ProClients lose their exclusive lock. -          * - Done before the CameraClient can initialize the HAL device, -          *   since we want to be able to close it before they get to initialize -          */ -        if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { -            Vector<wp<ProClient> > proClients(mProClientList[cameraId]); -            Vector<wp<ProClient> >::const_iterator it; - -            for (it = proClients.begin(); it != proClients.end(); ++it) { -                sp<ProClient> proCl = it->promote(); -                if (proCl.get() != NULL) { -                    proCl->onExclusiveLockStolen(); -                } +    // Update the status for this camera state, then send the onStatusChangedCallbacks to each +    // of the listeners with both the mStatusStatus and mStatusListenerLock held +    state->updateStatus(status, cameraId, rejectSourceStates, [this] +            (const String8& cameraId, ICameraServiceListener::Status status) { + +            // Update torch status +            if (status == ICameraServiceListener::STATUS_NOT_PRESENT || +                status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { +                // Update torch status to not available when the camera device becomes not present +                // or not available. +                onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE); +            } else if (status == ICameraServiceListener::STATUS_PRESENT) { +                // Update torch status to available when the camera device becomes present or +                // available +                onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);              } -        } -        if (status == ICameraServiceListener::STATUS_NOT_PRESENT || -            status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { -            // update torch status to not available when the camera device -            // becomes not present or not available. -            onTorchStatusChanged(String8::format("%d", cameraId), -                    ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE); -        } else if (status == ICameraServiceListener::STATUS_PRESENT) { -            // update torch status to available when the camera device becomes -            // present or available -            onTorchStatusChanged(String8::format("%d", cameraId), -                    ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); -        } +            Mutex::Autolock lock(mStatusListenerLock); -        Vector<sp<ICameraServiceListener> >::const_iterator it; -        for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { -            (*it)->onStatusChanged(status, cameraId); -        } -    } -} - -ICameraServiceListener::Status CameraService::getStatus(int cameraId) const { -    if (cameraId < 0 || cameraId >= MAX_CAMERAS) { -        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); -        return ICameraServiceListener::STATUS_UNKNOWN; -    } - -    Mutex::Autolock al(mStatusMutex); -    return mStatusList[cameraId]; +            for (auto& listener : mListenerList) { +                // TODO: Refactor status listeners to use strings for Camera IDs and remove this. +                int id = cameraIdToInt(cameraId); +                if (id != -1) listener->onStatusChanged(status, id); +            } +        });  }  status_t CameraService::getTorchStatusLocked( diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 22afc8c..53420e5 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -38,11 +38,15 @@  #include <camera/ICameraServiceListener.h>  #include "CameraFlashlight.h" -  #include "common/CameraModule.h" +#include "utils/AutoConditionLock.h" +#include "utils/ClientManager.h" +#include "utils/RingBuffer.h" -/* This needs to be increased if we can have more cameras */ -#define MAX_CAMERAS 2 +#include <set> +#include <string> +#include <map> +#include <memory>  namespace android { @@ -62,6 +66,34 @@ public:      class Client;      class BasicClient; +    enum apiLevel { +        API_1 = 1, +        API_2 = 2 +    }; + +    // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java) +    static const int PROCESS_STATE_NONEXISTENT = -1; +    static const int PROCESS_STATE_PERSISTENT = 0; +    static const int PROCESS_STATE_PERSISTENT_UI = 1; +    static const int PROCESS_STATE_TOP = 2; +    static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; +    static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; +    static const int PROCESS_STATE_BACKUP = 5; +    static const int PROCESS_STATE_HEAVY_WEIGHT = 6; +    static const int PROCESS_STATE_SERVICE = 7; +    static const int PROCESS_STATE_RECEIVER = 8; +    static const int PROCESS_STATE_HOME = 9; +    static const int PROCESS_STATE_LAST_ACTIVITY = 10; +    static const int PROCESS_STATE_CACHED_ACTIVITY = 11; +    static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; +    static const int PROCESS_STATE_CACHED_EMPTY = 13; + +    // 3 second busy timeout when other clients are connecting +    static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000; + +    // Default number of messages to store in eviction log +    static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50; +      // Implementation of BinderService<T>      static char const* getServiceName() { return "media.camera"; } @@ -70,8 +102,8 @@ public:      /////////////////////////////////////////////////////////////////////      // HAL Callbacks -    virtual void        onDeviceStatusChanged(int cameraId, -                                              int newStatus); +    virtual void        onDeviceStatusChanged(camera_device_status_t cameraId, +                                              camera_device_status_t newStatus);      virtual void        onTorchStatusChanged(const String8& cameraId,                                               ICameraServiceListener::TorchStatus                                                     newStatus); @@ -132,7 +164,6 @@ public:      /////////////////////////////////////////////////////////////////////      // Client functionality -    virtual void        removeClientByRemote(const wp<IBinder>& remoteBinder);      enum sound_kind {          SOUND_SHUTTER = 0, @@ -155,11 +186,6 @@ public:      /////////////////////////////////////////////////////////////////////      // CameraClient functionality -    // returns plain pointer of client. Note that mClientLock should be acquired to -    // prevent the client from destruction. The result can be NULL. -    virtual BasicClient* getClientByIdUnsafe(int cameraId); -    virtual Mutex*      getClientLockById(int cameraId); -      class BasicClient : public virtual RefBase {      public:          virtual status_t    initialize(CameraModule *module) = 0; @@ -176,6 +202,15 @@ public:          virtual status_t    dump(int fd, const Vector<String16>& args) = 0; +        // Return the package name for this client +        virtual String16 getPackageName() const; + +        // Notify client about a fatal error +        virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, +                const CaptureResultExtras& resultExtras) = 0; + +        // Get the PID of the application client using this +        virtual int getClientPid() const;      protected:          BasicClient(const sp<CameraService>& cameraService,                  const sp<IBinder>& remoteCallback, @@ -202,6 +237,7 @@ public:          pid_t                           mClientPid;          uid_t                           mClientUid;      // immutable after constructor          pid_t                           mServicePid;     // immutable after constructor +        bool                            mDisconnected;          // - The app-side Binder interface to receive callbacks from us          sp<IBinder>                     mRemoteBinder;   // immutable after constructor @@ -210,10 +246,6 @@ public:          status_t                        startCameraOps();          status_t                        finishCameraOps(); -        // Notify client about a fatal error -        virtual void                    notifyError( -                ICameraDeviceCallbacks::CameraErrorCode errorCode, -                const CaptureResultExtras& resultExtras) = 0;      private:          AppOpsManager                   mAppOpsManager; @@ -285,13 +317,11 @@ public:              return asBinder(this);          } -    protected: -        static Mutex*        getClientLockFromCookie(void* user); -        // convert client from cookie. Client lock should be acquired before getting Client. -        static Client*       getClientFromCookie(void* user); -          virtual void         notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,                                           const CaptureResultExtras& resultExtras); +    protected: +        // Convert client from cookie. +        static sp<CameraService::Client> getClientFromCookie(void* user);          // Initialized in constructor @@ -338,54 +368,230 @@ public:          // Callbacks from camera service          virtual void          onExclusiveLockStolen() = 0; -    protected:          virtual void          notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,                                            const CaptureResultExtras& resultExtras); +    protected:          sp<IProCameraCallbacks> mRemoteCallback;      }; // class ProClient +    typedef std::shared_ptr<resource_policy::ClientDescriptor<String8, +            sp<CameraService::BasicClient>>> DescriptorPtr; + +    /** +     * A container class for managing active camera clients that are using HAL devices.  Active +     * clients are represented by ClientDescriptor objects that contain strong pointers to the +     * actual BasicClient subclass binder interface implementation. +     * +     * This class manages the eviction behavior for the camera clients.  See the parent class +     * implementation in utils/ClientManager for the specifics of this behavior. +     */ +    class CameraClientManager : +            public resource_policy::ClientManager<String8, sp<CameraService::BasicClient>> { +    public: +        virtual ~CameraClientManager(); + +        /** +         * Return a strong pointer to the active BasicClient for this camera ID, or an empty +         * if none exists. +         */ +        sp<CameraService::BasicClient> getCameraClient(const String8& id) const; + +        /** +         * Return a string describing the current state. +         */ +        String8 toString() const; + +        /** +         * Make a ClientDescriptor object wrapping the given BasicClient strong pointer. +         */ +        static DescriptorPtr makeClientDescriptor(const String8& key, const sp<BasicClient>& value, +                int32_t cost, const std::set<String8>& conflictingKeys, int32_t priority, +                int32_t ownerId); + +        /** +         * Make a ClientDescriptor object wrapping the given BasicClient strong pointer with +         * values intialized from a prior ClientDescriptor. +         */ +        static DescriptorPtr makeClientDescriptor(const sp<BasicClient>& value, +                const CameraService::DescriptorPtr& partial); + +    }; // class CameraClientManager +  private: +    /** +     * Container class for the state of each logical camera device, including: ID, status, and +     * dependencies on other devices.  The mapping of camera ID -> state saved in mCameraStates +     * represents the camera devices advertised by the HAL (and any USB devices, when we add +     * those). +     * +     * This container does NOT represent an active camera client.  These are represented using +     * the ClientDescriptors stored in mActiveClientManager. +     */ +    class CameraState { +    public: +        /** +         * Make a new CameraState and set the ID, cost, and conflicting devices using the values +         * returned in the HAL's camera_info struct for each device. +         */ +        CameraState(const String8& id, int cost, const std::set<String8>& conflicting); +        virtual ~CameraState(); + +        /** +         * Return the status for this device. +         * +         * This method acquires mStatusLock. +         */ +        ICameraServiceListener::Status getStatus() const; + +        /** +         * This function updates the status for this camera device, unless the given status +         * is in the given list of rejected status states, and execute the function passed in +         * with a signature onStatusUpdateLocked(const String8&, ICameraServiceListener::Status) +         * if the status has changed. +         * +         * This method is idempotent, and will not result in the function passed to +         * onStatusUpdateLocked being called more than once for the same arguments. +         * This method aquires mStatusLock. +         */ +        template<class Func> +        void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, +                std::initializer_list<ICameraServiceListener::Status> rejectSourceStates, +                Func onStatusUpdatedLocked); + +        /** +         * Return the last set CameraParameters object generated from the information returned by +         * the HAL for this device (or an empty CameraParameters object if none has been set). +         */ +        CameraParameters getShimParams() const; + +        /** +         * Set the CameraParameters for this device. +         */ +        void setShimParams(const CameraParameters& params); + +        /** +         * Return the resource_cost advertised by the HAL for this device. +         */ +        int getCost() const; + +        /** +         * Return a set of the IDs of conflicting devices advertised by the HAL for this device. +         */ +        std::set<String8> getConflicting() const; + +        /** +         * Return the ID of this camera device. +         */ +        String8 getId() const; + +    private: +        const String8 mId; +        ICameraServiceListener::Status mStatus; // protected by mStatusLock +        const int mCost; +        std::set<String8> mConflicting; +        mutable Mutex mStatusLock; +        CameraParameters mShimParams; +    }; // class CameraState +      // Delay-load the Camera HAL module      virtual void onFirstRef(); -    // Step 1. Check if we can connect, before we acquire the service lock. -    status_t            validateConnect(int cameraId, -                                        /*inout*/ -                                        int& clientUid) const; +    // Check if we can connect, before we acquire the service lock. +    status_t validateConnect(const String8& cameraId, /*inout*/int& clientUid) const; -    // Step 2. Check if we can connect, after we acquire the service lock. -    bool                canConnectUnsafe(int cameraId, -                                         const String16& clientPackageName, -                                         const sp<IBinder>& remoteCallback, -                                         /*out*/ -                                         sp<BasicClient> &client); +    // Handle active client evictions, and update service state. +    // Only call with with mServiceLock held. +    status_t handleEvictionsLocked(const String8& cameraId, int clientPid, +        apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName, +        /*out*/ +        sp<BasicClient>* client, +        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial); -    // When connection is successful, initialize client and track its death -    status_t            connectFinishUnsafe(const sp<BasicClient>& client, -                                            const sp<IBinder>& remoteCallback); +    // Single implementation shared between the various connect calls +    template<class CALLBACK, class CLIENT> +    status_t connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int halVersion, +            const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel, +            bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device); -    virtual sp<BasicClient>  getClientByRemote(const wp<IBinder>& cameraClient); +    // Lock guarding camera service state      Mutex               mServiceLock; -    // either a Client or CameraDeviceClient -    wp<BasicClient>     mClient[MAX_CAMERAS];  // protected by mServiceLock -    Mutex               mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks -    int                 mNumberOfCameras; -    typedef wp<ProClient> weak_pro_client_ptr; -    Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS]; +    // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients +    std::shared_ptr<WaitableMutexWrapper> mServiceLockWrapper; + +    // Return NO_ERROR if the device with a give ID can be connected to +    status_t checkIfDeviceIsUsable(const String8& cameraId) const; + +    // Container for managing currently active application-layer clients +    CameraClientManager mActiveClientManager; -    // needs to be called with mServiceLock held -    sp<BasicClient>     findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex); -    sp<ProClient>       findProClientUnsafe( -                                     const wp<IBinder>& cameraCallbacksRemote); +    // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock +    std::map<String8, std::shared_ptr<CameraState>> mCameraStates; -    // atomics to record whether the hardware is allocated to some client. -    volatile int32_t    mBusy[MAX_CAMERAS]; -    void                setCameraBusy(int cameraId); -    void                setCameraFree(int cameraId); +    // Mutex guarding mCameraStates map +    mutable Mutex mCameraStatesLock; + +    // Circular buffer for storing event logging for dumps +    RingBuffer<String8> mEventLog; + +    /** +     * Get the camera state for a given camera id. +     * +     * This acquires mCameraStatesLock. +     */ +    std::shared_ptr<CameraService::CameraState> getCameraState(const String8& cameraId) const; + +    /** +     * Evict client who's remote binder has died.  Returns true if this client was in the active +     * list and was disconnected. +     * +     * This method acquires mServiceLock. +     */ +    bool evictClientIdByRemote(const wp<IBinder>& cameraClient); + +    /** +     * Remove the given client from the active clients list; does not disconnect the client. +     * +     * This method acquires mServiceLock. +     */ +    void removeByClient(const BasicClient* client); + +    /** +     * Add new client to active clients list after conflicting clients have disconnected using the +     * values set in the partial descriptor passed in to construct the actual client descriptor. +     * This is typically called at the end of a connect call. +     * +     * This method must be called with mServiceLock held. +     */ +    void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc); + +    /** +     * Returns the integer corresponding to the given camera ID string, or -1 on failure. +     */ +    static int cameraIdToInt(const String8& cameraId); + +    /** +     * Remove a single client corresponding to the given camera id from the list of active clients. +     * If none exists, return an empty strongpointer. +     * +     * This method must be called with mServiceLock held. +     */ +    sp<CameraService::BasicClient> removeClientLocked(const String8& cameraId); + +    /** +     * Add a event log message that a client has been disconnected. +     */ +    void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage); + +    /** +     * Add a event log message that a client has been connected. +     */ +    void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage); + +    int                 mNumberOfCameras;      // sounds      MediaPlayer*        newMediaPlayer(const char *file); @@ -396,24 +602,21 @@ private:      CameraModule*     mModule; -    Vector<sp<ICameraServiceListener> > -                        mListenerList; - -    // guard only mStatusList and the broadcasting of ICameraServiceListener -    mutable Mutex       mStatusMutex; -    ICameraServiceListener::Status -                        mStatusList[MAX_CAMERAS]; +    // Guarded by mStatusListenerMutex +    std::vector<sp<ICameraServiceListener>> mListenerList; +    Mutex       mStatusListenerLock; -    // Read the current status (locks mStatusMutex) -    ICameraServiceListener::Status -                        getStatus(int cameraId) const; - -    typedef Vector<ICameraServiceListener::Status> StatusVector; -    // Broadcast the new status if it changed (locks the service mutex) -    void                updateStatus( -                            ICameraServiceListener::Status status, -                            int32_t cameraId, -                            const StatusVector *rejectSourceStates = NULL); +    /** +     * Update the status for the given camera id (if that device exists), and broadcast the +     * status update to all current ICameraServiceListeners if the status has changed.  Any +     * statuses in rejectedSourceStates will be ignored. +     * +     * This method must be idempotent. +     * This method acquires mStatusLock and mStatusListenerLock. +     */ +    void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, +            std::initializer_list<ICameraServiceListener::Status> rejectedSourceStates); +    void updateStatus(ICameraServiceListener::Status status, const String8& cameraId);      // flashlight control      sp<CameraFlashlight> mFlashlight; @@ -435,9 +638,6 @@ private:      void onTorchStatusChangedLocked(const String8& cameraId,              ICameraServiceListener::TorchStatus newStatus); -    // validate the camera id for use of setting a torch mode. -    bool validCameraIdForSetTorchMode(const String8& cameraId); -      // get a camera's torch status. mTorchStatusMutex should be locked.      status_t getTorchStatusLocked(const String8 &cameraId,              ICameraServiceListener::TorchStatus *status) const; @@ -451,19 +651,9 @@ private:      // Helpers -    bool                isValidCameraId(int cameraId); -      bool                setUpVendorTags();      /** -     * A mapping of camera ids to CameraParameters returned by that camera device. -     * -     * This cache is used to generate CameraCharacteristic metadata when using -     * the HAL1 shim. -     */ -    KeyedVector<int, CameraParameters>    mShimParams; - -    /**       * Initialize and cache the metadata used by the HAL1 shim for a given cameraId.       *       * Returns OK on success, or a negative error code. @@ -486,25 +676,190 @@ private:       */      status_t            generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo); +    static int getCallingPid(); + +    static int getCallingUid(); +      /** -     * Connect a new camera client.  This should only be used while holding the -     * mutex for mServiceLock. -     * -     * Returns OK on success, or a negative error code. +     * Get the current system time as a formatted string.       */ -    status_t            connectHelperLocked( -            /*out*/ -            sp<Client>& client, -            /*in*/ -            const sp<ICameraClient>& cameraClient, -            int cameraId, -            const String16& clientPackageName, -            int clientUid, -            int callingPid, -            int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED, -            bool legacyMode = false); +    static String8 getFormattedCurrentTime(); + +    /** +     * Get the camera eviction priority from the current process state given by ActivityManager. +     */ +    static int getCameraPriorityFromProcState(int procState); + +    static status_t makeClient(const sp<CameraService>& cameraService, +            const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId, +            int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, +            int halVersion, int deviceVersion, apiLevel effectiveApiLevel, +            /*out*/sp<BasicClient>* client);  }; +template<class Func> +void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status, +        const String8& cameraId, +        std::initializer_list<ICameraServiceListener::Status> rejectSourceStates, +        Func onStatusUpdatedLocked) { +    Mutex::Autolock lock(mStatusLock); +    ICameraServiceListener::Status oldStatus = mStatus; +    mStatus = status; + +    if (oldStatus == status) { +        return; +    } + +    ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__, +            cameraId.string(), oldStatus, status); + +    if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && +        (status != ICameraServiceListener::STATUS_PRESENT && +         status != ICameraServiceListener::STATUS_ENUMERATING)) { + +        ALOGW("%s: From NOT_PRESENT can only transition into PRESENT or ENUMERATING", +                __FUNCTION__); +        mStatus = oldStatus; +        return; +    } + +    /** +     * Sometimes we want to conditionally do a transition. +     * For example if a client disconnects, we want to go to PRESENT +     * only if we weren't already in NOT_PRESENT or ENUMERATING. +     */ +    for (auto& rejectStatus : rejectSourceStates) { +        if (oldStatus == rejectStatus) { +            ALOGV("%s: Rejecting status transition for Camera ID %s,  since the source " +                    "state was was in one of the bad states.", __FUNCTION__, cameraId.string()); +            mStatus = oldStatus; +            return; +        } +    } + +    onStatusUpdatedLocked(cameraId, status); +} + + +template<class CALLBACK, class CLIENT> +status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, +        int halVersion, const String16& clientPackageName, int clientUid, +        apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly, +        /*out*/sp<CLIENT>& device) { +    status_t ret = NO_ERROR; +    String8 clientName8(clientPackageName); +    int clientPid = getCallingPid(); + +    ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and " +            "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; +        } + +        // Check the shim parameters after acquiring lock, if they have already been updated and +        // we were doing a shim update, return immediately +        if (shimUpdateOnly) { +            auto cameraState = getCameraState(cameraId); +            if (cameraState != nullptr) { +                if (!cameraState->getShimParams().isEmpty()) return NO_ERROR; +            } +        } + +        sp<BasicClient> clientTmp = nullptr; +        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial; +        if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel, +                IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp, +                /*out*/&partial)) != NO_ERROR) { +            return ret; +        } + +        if (clientTmp.get() != nullptr) { +            // Handle special case for API1 MediaRecorder where the existing client is returned +            device = static_cast<CLIENT*>(clientTmp.get()); +            return NO_ERROR; +        } + +        // give flashlight a chance to close devices if necessary. +        mFlashlight->prepareDeviceOpen(cameraId); + +        // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs +        int id = cameraIdToInt(cameraId); +        if (id == -1) { +            ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__, +                    cameraId.string()); +            return BAD_VALUE; +        } + +        int facing = -1; +        int deviceVersion = getDeviceVersion(id, /*out*/&facing); +        sp<BasicClient> tmp = nullptr; +        if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid, +                clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel, +                /*out*/&tmp)) != NO_ERROR) { +            return ret; +        } +        client = static_cast<CLIENT*>(tmp.get()); + +        LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state", +                __FUNCTION__); + +        if ((ret = client->initialize(mModule)) != OK) { +            ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); +            return ret; +        } + +        sp<IBinder> remoteCallback = client->getRemote(); +        if (remoteCallback != nullptr) { +            remoteCallback->linkToDeath(this); +        } + +        // Update shim paremeters for legacy clients +        if (effectiveApiLevel == API_1) { +            // Assume we have always received a Client subclass for API1 +            sp<Client> shimClient = reinterpret_cast<Client*>(client.get()); +            String8 rawParams = shimClient->getParameters(); +            CameraParameters params(rawParams); + +            auto cameraState = getCameraState(cameraId); +            if (cameraState != nullptr) { +                cameraState->setShimParams(params); +            } else { +                ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.", +                        __FUNCTION__, cameraId.string()); +            } +        } + +        if (shimUpdateOnly) { +            // If only updating legacy shim parameters, immediately disconnect client +            mServiceLock.unlock(); +            client->disconnect(); +            mServiceLock.lock(); +        } else { +            // Otherwise, add client to active clients list +            finishConnectLocked(client, partial); +        } +    } // lock is destroyed, allow further connect calls + +    // Important: release the mutex here so the client can call back into the service from its +    // destructor (can be at the end of the call) +    device = client; +    return NO_ERROR; +} +  } // namespace android  #endif diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index 5dbdeb2..6f44aee 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -163,11 +163,9 @@ Camera2Client::~Camera2Client() {  status_t Camera2Client::dump(int fd, const Vector<String16>& args) {      String8 result; -    result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n", -            mCameraId, +    result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId,              (getRemoteCallback() != NULL ?                      (IInterface::asBinder(getRemoteCallback()).get()) : NULL), -            String8(mClientPackageName).string(),              mClientPid);      result.append("  State: ");  #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index 6bea3b6..e552633 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -99,12 +99,7 @@ status_t CameraClient::initialize(CameraModule *module) {  // tear down the client  CameraClient::~CameraClient() { -    // this lock should never be NULL -    Mutex* lock = mCameraService->getClientLockById(mCameraId); -    lock->lock();      mDestructionStarted = true; -    // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect -    lock->unlock();      int callingPid = getCallingPid();      LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this); @@ -116,11 +111,11 @@ status_t CameraClient::dump(int fd, const Vector<String16>& args) {      const size_t SIZE = 256;      char buffer[SIZE]; -    size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n", +    size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n",              mCameraId,              (getRemoteCallback() != NULL ?                      IInterface::asBinder(getRemoteCallback()).get() : NULL), -            mClientPid); +            mClientUid);      len = (len > SIZE - 1) ? SIZE - 1 : len;      write(fd, buffer, len); @@ -677,6 +672,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) {                  LOG1("lockIfMessageWanted(%d): waited for %d ms",                      msgType, sleepCount * CHECK_MESSAGE_INTERVAL);              } + +            // If messages are no longer enabled after acquiring lock, release and drop message +            if ((mMsgEnabled & msgType) == 0) { +                mLock.unlock(); +                break; +            } +              return true;          }          if (sleepCount++ == 0) { @@ -702,26 +704,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) {  //      (others)                        c->dataCallback  // dataCallbackTimestamp  //      (others)                        c->dataCallbackTimestamp -// -// NOTE: the *Callback functions grab mLock of the client before passing -// control to handle* functions. So the handle* functions must release the -// lock before calling the ICameraClient's callbacks, so those callbacks can -// invoke methods in the Client class again (For example, the preview frame -// callback may want to releaseRecordingFrame). The handle* functions must -// release the lock after all accesses to member variables, so it must be -// handled very carefully.  void CameraClient::notifyCallback(int32_t msgType, int32_t ext1,          int32_t ext2, void* user) {      LOG2("notifyCallback(%d)", msgType); -    Mutex* lock = getClientLockFromCookie(user); -    if (lock == NULL) return; -    Mutex::Autolock alock(*lock); - -    CameraClient* client = -            static_cast<CameraClient*>(getClientFromCookie(user)); -    if (client == NULL) return; +    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); +    if (client.get() == nullptr) return;      if (!client->lockIfMessageWanted(msgType)) return; @@ -740,13 +729,8 @@ void CameraClient::dataCallback(int32_t msgType,          const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {      LOG2("dataCallback(%d)", msgType); -    Mutex* lock = getClientLockFromCookie(user); -    if (lock == NULL) return; -    Mutex::Autolock alock(*lock); - -    CameraClient* client = -            static_cast<CameraClient*>(getClientFromCookie(user)); -    if (client == NULL) return; +    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); +    if (client.get() == nullptr) return;      if (!client->lockIfMessageWanted(msgType)) return;      if (dataPtr == 0 && metadata == NULL) { @@ -778,13 +762,8 @@ void CameraClient::dataCallbackTimestamp(nsecs_t timestamp,          int32_t msgType, const sp<IMemory>& dataPtr, void* user) {      LOG2("dataCallbackTimestamp(%d)", msgType); -    Mutex* lock = getClientLockFromCookie(user); -    if (lock == NULL) return; -    Mutex::Autolock alock(*lock); - -    CameraClient* client = -            static_cast<CameraClient*>(getClientFromCookie(user)); -    if (client == NULL) return; +    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); +    if (client.get() == nullptr) return;      if (!client->lockIfMessageWanted(msgType)) return; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index acc092c..755628b 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -586,9 +586,7 @@ status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {              mCameraId,              (getRemoteCallback() != NULL ?                      IInterface::asBinder(getRemoteCallback()).get() : NULL) ); -    result.appendFormat("  Current client: %s (PID %d, UID %u)\n", -            String8(mClientPackageName).string(), -            mClientPid, mClientUid); +    result.appendFormat("  Current client UID %u\n", mClientUid);      result.append("  State:\n");      result.appendFormat("    Request ID counter: %d\n", mRequestIdCounter); diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.cpp b/services/camera/libcameraservice/utils/AutoConditionLock.cpp new file mode 100644 index 0000000..c8ee965 --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "AutoConditionLock.h" + +namespace android { + +WaitableMutexWrapper::WaitableMutexWrapper(Mutex* mutex) : mMutex{mutex}, mState{false} {} + +WaitableMutexWrapper::~WaitableMutexWrapper() {} + +// Locks manager-owned mutex +AutoConditionLock::AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager) : +        mManager{manager}, mAutoLock{manager->mMutex} {} + +// Unlocks manager-owned mutex +AutoConditionLock::~AutoConditionLock() { +    // Unset the condition and wake everyone up before releasing lock +    mManager->mState = false; +    mManager->mCondition.broadcast(); +} + +std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire( +        const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime) { + +    if (manager == nullptr || manager->mMutex == nullptr) { +        // Bad input, return null +        return std::unique_ptr<AutoConditionLock>{nullptr}; +    } + +    // Acquire scoped lock +    std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager)); + +    // Figure out what time in the future we should hit the timeout +    nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + waitTime; + +    // Wait until we timeout, or success +    while(manager->mState) { +        status_t ret = manager->mCondition.waitRelative(*(manager->mMutex), waitTime); +        if (ret != NO_ERROR) { +            // Timed out or whatever, return null +            return std::unique_ptr<AutoConditionLock>{nullptr}; +        } +        waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC); +    } + +    // Set the condition and return +    manager->mState = true; +    return scopedLock; +} + +std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire( +        const std::shared_ptr<WaitableMutexWrapper>& manager) { + +    if (manager == nullptr || manager->mMutex == nullptr) { +        // Bad input, return null +        return std::unique_ptr<AutoConditionLock>{nullptr}; +    } + +    // Acquire scoped lock +    std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager)); + +    // Wait until we timeout, or success +    while(manager->mState) { +        status_t ret = manager->mCondition.wait(*(manager->mMutex)); +        if (ret != NO_ERROR) { +            // Timed out or whatever, return null +            return std::unique_ptr<AutoConditionLock>{nullptr}; +        } +    } + +    // Set the condition and return +    manager->mState = true; +    return scopedLock; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.h b/services/camera/libcameraservice/utils/AutoConditionLock.h new file mode 100644 index 0000000..9a3eafc --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 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. + */ + + +#ifndef ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H +#define ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H + +#include <utils/Timers.h> +#include <utils/Condition.h> +#include <utils/Errors.h> +#include <utils/Mutex.h> + +#include <memory> + +namespace android { + +/** + * WaitableMutexWrapper can be used with AutoConditionLock to construct scoped locks for the + * wrapped Mutex with timeouts for lock acquisition. + */ +class WaitableMutexWrapper { +    friend class AutoConditionLock; +public: +    /** +     * Construct the ConditionManger with the given Mutex. +     */ +    WaitableMutexWrapper(Mutex* mutex); + +    virtual ~WaitableMutexWrapper(); +private: +    Mutex* mMutex; +    bool mState; +    Condition mCondition; +}; + +/** + * AutoConditionLock is a scoped lock similar to Mutex::Autolock, but allows timeouts to be + * specified for lock acquisition. + * + * AutoConditionLock is used with a WaitableMutexWrapper to lock/unlock the WaitableMutexWrapper's + * wrapped Mutex, and wait/set/signal the WaitableMutexWrapper's wrapped condition. To use this, + * call AutoConditionLock::waitAndAcquire to get an instance.  This will: + *      - Lock the given WaitableMutexWrapper's mutex. + *      - Wait for the WaitableMutexWrapper's condition to become false, or timeout. + *      - Set the WaitableMutexWrapper's condition to true. + * + * When the AutoConditionLock goes out of scope and is destroyed, this will: + *      - Set the WaitableMutexWrapper's condition to false. + *      - Signal threads waiting on this condition to wakeup. + *      - Release WaitableMutexWrapper's mutex. + */ +class AutoConditionLock final { +public: +    AutoConditionLock() = delete; +    AutoConditionLock(const AutoConditionLock& other) = delete; +    AutoConditionLock & operator=(const AutoConditionLock&) = delete; + +    ~AutoConditionLock(); + +    /** +     * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting up to waitTime +     * nanoseconds to acquire the WaitableMutexWrapper's wrapped lock. +     * +     * Return an empty unique_ptr if this fails, or a timeout occurs. +     */ +    static std::unique_ptr<AutoConditionLock> waitAndAcquire( +            const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime); + +    /** +     * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting indefinitely to +     * acquire the WaitableMutexWrapper's wrapped lock. +     * +     * Return an empty unique_ptr if this fails. +     */ +    static std::unique_ptr<AutoConditionLock> waitAndAcquire( +            const std::shared_ptr<WaitableMutexWrapper>& manager); +private: +    AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager); + +    std::shared_ptr<WaitableMutexWrapper> mManager; +    Mutex::Autolock mAutoLock; +}; + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h new file mode 100644 index 0000000..ad5486d --- /dev/null +++ b/services/camera/libcameraservice/utils/ClientManager.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H +#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H + +#include <utils/Mutex.h> + +#include <algorithm> +#include <utility> +#include <vector> +#include <set> +#include <map> +#include <memory> + +namespace android { +namespace resource_policy { + +// -------------------------------------------------------------------------------- + +/** + * The ClientDescriptor class is a container for a given key/value pair identifying a shared + * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used + * in determining eviction behavior. + * + * Aside from the priority, these values are immutable once the ClientDescriptor has been + * constructed. + */ +template<class KEY, class VALUE> +class ClientDescriptor final { +public: +    ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, +            const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId); +    ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys, +            int32_t priority, int32_t ownerId); + +    ~ClientDescriptor(); + +    /** +     * Return the key for this descriptor. +     */ +    const KEY& getKey() const; + +    /** +     * Return the value for this descriptor. +     */ +    const VALUE& getValue() const; + +    /** +     * Return the cost for this descriptor. +     */ +    int32_t getCost() const; + +    /** +     * Return the priority for this descriptor. +     */ +    int32_t getPriority() const; + +    /** +     * Return the owner ID for this descriptor. +     */ +    int32_t getOwnerId() const; + +    /** +     * Return true if the given key is in this descriptor's conflicting keys list. +     */ +    bool isConflicting(const KEY& key) const; + +    /** +     * Return the set of all conflicting keys for this descriptor. +     */ +    std::set<KEY> getConflicting() const; + +    /** +     * Set the proirity for this descriptor. +     */ +    void setPriority(int32_t priority); + +    // This class is ordered by key +    template<class K, class V> +    friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b); + +private: +    KEY mKey; +    VALUE mValue; +    int32_t mCost; +    std::set<KEY> mConflicting; +    int32_t mPriority; +    int32_t mOwnerId; +}; // class ClientDescriptor + +template<class K, class V> +bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) { +    return a.mKey < b.mKey; +} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, +        const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key}, +        mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority}, +        mOwnerId{ownerId} {} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, +        std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) : +        mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost}, +        mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority}, +        mOwnerId{ownerId} {} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {} + +template<class KEY, class VALUE> +const KEY& ClientDescriptor<KEY, VALUE>::getKey() const { +    return mKey; +} + +template<class KEY, class VALUE> +const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const { +    return mValue; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getCost() const { +    return mCost; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getPriority() const { +    return mPriority; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const { +    return mOwnerId; +} + +template<class KEY, class VALUE> +bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const { +    if (key == mKey) return true; +    for (const auto& x : mConflicting) { +        if (key == x) return true; +    } +    return false; +} + +template<class KEY, class VALUE> +std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const { +    return mConflicting; +} + +template<class KEY, class VALUE> +void ClientDescriptor<KEY, VALUE>::setPriority(int32_t priority) { +    mPriority = priority; +} + +// -------------------------------------------------------------------------------- + +/** + * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction + * behavior for handling shared resource access. + * + * When adding a new descriptor, eviction behavior is as follows: + *   - Keys are unique, adding a descriptor with the same key as an existing descriptor will + *     result in the lower-priority of the two being removed.  Priority ties result in the + *     LRU descriptor being evicted (this means the incoming descriptor be added in this case). + *   - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list + *     will be removed if they have an equal or lower priority than the incoming descriptor; + *     if any have a higher priority, the incoming descriptor is removed instead. + *   - If the sum of all descriptors' costs, including the incoming descriptor's, is more than + *     the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower + *     priority, and a different owner will be evicted in LRU order until either the cost is less + *     than the max cost, or all descriptors meeting this criteria have been evicted and the + *     incoming descriptor has the highest priority.  Otherwise, the incoming descriptor is + *     removed instead. + */ +template<class KEY, class VALUE> +class ClientManager { +public: +    // The default maximum "cost" allowed before evicting +    static constexpr int32_t DEFAULT_MAX_COST = 100; + +    ClientManager(); +    ClientManager(int32_t totalCost); + +    /** +     * Add a given ClientDescriptor to the managed list.  ClientDescriptors for clients that +     * are evicted by this action are returned in a vector. +     * +     * This may return the ClientDescriptor passed in if it would be evicted. +     */ +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict( +            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client); + +    /** +     * Given a map containing owner (pid) -> priority mappings, update the priority of each +     * ClientDescriptor with an owner in this mapping. +     */ +    void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList); + +    /** +     * Remove all ClientDescriptors. +     */ +    void removeAll(); + +    /** +     * Remove and return the ClientDescriptor with a given key. +     */ +    std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key); + +    /** +     * Remove the given ClientDescriptor. +     */ +    void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value); + +    /** +     * Return a vector of the ClientDescriptors that would be evicted by adding the given +     * ClientDescriptor. +     * +     * This may return the ClientDescriptor passed in if it would be evicted. +     */ +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict( +            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const; + +    /** +     * Return a vector of active ClientDescriptors that prevent this client from being added. +     */ +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients( +            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const; + +    /** +     * Return a vector containing all currently active ClientDescriptors. +     */ +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const; + +    /** +     * Return a vector containing all keys of currently active ClientDescriptors. +     */ +    std::vector<KEY> getAllKeys() const; + +    /** +     * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates +     * will be removed). +     */ +    std::vector<int32_t> getAllOwners() const; + +    /** +     * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer +     * if none exists. +     */ +    std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const; + +protected: +    ~ClientManager(); + +private: + +    /** +     * Return a vector of the ClientDescriptors that would be evicted by adding the given +     * ClientDescriptor.  If returnIncompatibleClients is set to true, instead, return the +     * vector of ClientDescriptors that are higher priority than the incoming client and +     * either conflict with this client, or contribute to the resource cost if that would +     * prevent the incoming client from being added. +     * +     * This may return the ClientDescriptor passed in. +     */ +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked( +            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client, +            bool returnIncompatibleClients = false) const; + +    int64_t getCurrentCostLocked() const; + +    mutable Mutex mLock; +    int32_t mMaxCost; +    // LRU ordered, most recent at end +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients; +}; // class ClientManager + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::ClientManager() : +        ClientManager(DEFAULT_MAX_COST) {} + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {} + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::~ClientManager() {} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict( +        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const { +    Mutex::Autolock lock(mLock); +    return wouldEvictLocked(client); +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::getIncompatibleClients( +        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const { +    Mutex::Autolock lock(mLock); +    return wouldEvictLocked(client, /*returnIncompatibleClients*/true); +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::wouldEvictLocked( +        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client, +        bool returnIncompatibleClients) const { + +    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList; + +    // Disallow null clients, return input +    if (client == nullptr) { +        evictList.push_back(client); +        return evictList; +    } + +    const KEY& key = client->getKey(); +    int32_t cost = client->getCost(); +    int32_t priority = client->getPriority(); +    int32_t owner = client->getOwnerId(); + +    int64_t totalCost = getCurrentCostLocked() + cost; + +    // Determine the MRU of the owners tied for having the highest priority +    int32_t highestPriorityOwner = owner; +    int32_t highestPriority = priority; +    for (const auto& i : mClients) { +        int32_t curPriority = i->getPriority(); +        if (curPriority >= highestPriority) { +            highestPriority = curPriority; +            highestPriorityOwner = i->getOwnerId(); +        } +    } + +    if (highestPriority == priority) { +        // Switch back owner if the incoming client has the highest priority, as it is MRU +        highestPriorityOwner = owner; +    } + +    // Build eviction list of clients to remove +    for (const auto& i : mClients) { +        const KEY& curKey = i->getKey(); +        int32_t curCost = i->getCost(); +        int32_t curPriority = i->getPriority(); +        int32_t curOwner = i->getOwnerId(); + +        bool conflicting = (curKey == key || i->isConflicting(key) || +                client->isConflicting(curKey)); + +        if (!returnIncompatibleClients) { +            // Find evicted clients + +            if (conflicting && curPriority > priority) { +                // Pre-existing conflicting client with higher priority exists +                evictList.clear(); +                evictList.push_back(client); +                return evictList; +            } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) && +                    (curPriority <= priority) && +                    !(highestPriorityOwner == owner && owner == curOwner))) { +                // Add a pre-existing client to the eviction list if: +                // - We are adding a client with higher priority that conflicts with this one. +                // - The total cost including the incoming client's is more than the allowable +                //   maximum, and the client has a non-zero cost, lower priority, and a different +                //   owner than the incoming client when the incoming client has the +                //   highest priority. +                evictList.push_back(i); +                totalCost -= curCost; +            } +        } else { +            // Find clients preventing the incoming client from being added + +            if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) { +                // Pre-existing conflicting client with higher priority exists +                evictList.push_back(i); +            } +        } +    } + +    // Immediately return the incompatible clients if we are calculating these instead +    if (returnIncompatibleClients) { +        return evictList; +    } + +    // If the total cost is too high, return the input unless the input has the highest priority +    if (totalCost > mMaxCost && highestPriorityOwner != owner) { +        evictList.clear(); +        evictList.push_back(client); +        return evictList; +    } + +    return evictList; + +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict( +        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) { +    Mutex::Autolock lock(mLock); +    auto evicted = wouldEvictLocked(client); +    auto it = evicted.begin(); +    if (it != evicted.end() && *it == client) { +        return evicted; +    } + +    auto iter = evicted.cbegin(); + +    // Remove evicted clients from list +    mClients.erase(std::remove_if(mClients.begin(), mClients.end(), +        [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { +            if (curClientPtr->getKey() == (*iter)->getKey()) { +                iter++; +                return true; +            } +            return false; +        }), mClients.end()); + +    mClients.push_back(client); + +    return evicted; +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::getAll() const { +    Mutex::Autolock lock(mLock); +    return mClients; +} + +template<class KEY, class VALUE> +std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const { +    Mutex::Autolock lock(mLock); +    std::vector<KEY> keys(mClients.size()); +    for (const auto& i : mClients) { +        keys.push_back(i->getKey()); +    } +    return keys; +} + +template<class KEY, class VALUE> +std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const { +    Mutex::Autolock lock(mLock); +    std::set<int32_t> owners; +    for (const auto& i : mClients) { +        owners.emplace(i->getOwnerId()); +    } +    return std::vector<int32_t>(owners.begin(), owners.end()); +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::updatePriorities( +        const std::map<int32_t,int32_t>& ownerPriorityList) { +    Mutex::Autolock lock(mLock); +    for (auto& i : mClients) { +        auto j = ownerPriorityList.find(i->getOwnerId()); +        if (j != ownerPriorityList.end()) { +            i->setPriority(j->second); +        } +    } +} + +template<class KEY, class VALUE> +std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get( +        const KEY& key) const { +    Mutex::Autolock lock(mLock); +    for (const auto& i : mClients) { +        if (i->getKey() == key) return i; +    } +    return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr); +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::removeAll() { +    Mutex::Autolock lock(mLock); +    mClients.clear(); +} + +template<class KEY, class VALUE> +std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) { +    Mutex::Autolock lock(mLock); + +    std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret; + +    // Remove evicted clients from list +    mClients.erase(std::remove_if(mClients.begin(), mClients.end(), +        [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { +            if (curClientPtr->getKey() == key) { +                ret = curClientPtr; +                return true; +            } +            return false; +        }), mClients.end()); + +    return ret; +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::remove( +        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) { +    Mutex::Autolock lock(mLock); +    // Remove evicted clients from list +    mClients.erase(std::remove_if(mClients.begin(), mClients.end(), +        [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { +            if (curClientPtr == value) { +                return true; +            } +            return false; +        }), mClients.end()); +} + +template<class KEY, class VALUE> +int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const { +    int64_t totalCost = 0; +    for (const auto& x : mClients) { +            totalCost += x->getCost(); +    } +    return totalCost; +} + +// -------------------------------------------------------------------------------- + +}; // namespace resource_policy +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/services/camera/libcameraservice/utils/RingBuffer.h new file mode 100644 index 0000000..df7c00e --- /dev/null +++ b/services/camera/libcameraservice/utils/RingBuffer.h @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2015 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. + */ + + +#ifndef ANDROID_SERVICE_UTILS_RING_BUFFER_H +#define ANDROID_SERVICE_UTILS_RING_BUFFER_H + +#include <utils/Log.h> +#include <cutils/compiler.h> + +#include <iterator> +#include <utility> +#include <vector> + +namespace android { + +/** + * A RingBuffer class that maintains an array of objects that can grow up to a certain size. + * Elements added to the RingBuffer are inserted in the logical front of the buffer, and + * invalidate all current iterators for that RingBuffer object. + */ +template <class T> +class RingBuffer final { +public: + +    /** +     * Construct a RingBuffer that can grow up to the given length. +     */ +    RingBuffer(size_t length); + +    /** +     * Forward iterator to this class.  Implements an std:forward_iterator. +     */ +    class iterator : public std::iterator<std::forward_iterator_tag, T> { +    public: +        iterator(T* ptr, size_t size, size_t pos, size_t ctr); + +        iterator& operator++(); + +        iterator operator++(int); + +        bool operator==(const iterator& rhs); + +        bool operator!=(const iterator& rhs); + +        T& operator*(); + +        T* operator->(); + +    private: +        T* mPtr; +        size_t mSize; +        size_t mPos; +        size_t mCtr; +    }; + +    /** +     * Constant forward iterator to this class.  Implements an std:forward_iterator. +     */ +    class const_iterator : public std::iterator<std::forward_iterator_tag, T> { +    public: +        const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr); + +        const_iterator& operator++(); + +        const_iterator operator++(int); + +        bool operator==(const const_iterator& rhs); + +        bool operator!=(const const_iterator& rhs); + +        const T& operator*(); + +        const T* operator->(); + +    private: +        const T* mPtr; +        size_t mSize; +        size_t mPos; +        size_t mCtr; +    }; + +    /** +     * Adds item to the front of this RingBuffer.  If the RingBuffer is at its maximum length, +     * this will result in the last element being replaced (this is done using the element's +     * assignment operator). +     * +     * All current iterators are invalidated. +     */ +    void add(const T& item); + +    /** +     * Moves item to the front of this RingBuffer.  Following a call to this, item should no +     * longer be used.  If the RingBuffer is at its maximum length, this will result in the +     * last element being replaced (this is done using the element's assignment operator). +     * +     * All current iterators are invalidated. +     */ +    void add(T&& item); + +    /** +     * Construct item in-place in the front of this RingBuffer using the given arguments.  If +     * the RingBuffer is at its maximum length, this will result in the last element being +     * replaced (this is done using the element's assignment operator). +     * +     * All current iterators are invalidated. +     */ +    template <class... Args> +    void emplace(Args&&... args); + +    /** +     * Get an iterator to the front of this RingBuffer. +     */ +    iterator begin(); + +    /** +     * Get an iterator to the end of this RingBuffer. +     */ +    iterator end(); + +    /** +     * Get a const_iterator to the front of this RingBuffer. +     */ +    const_iterator begin() const; + +    /** +     * Get a const_iterator to the end of this RingBuffer. +     */ +    const_iterator end() const; + +    /** +     * Return a reference to the element at a given index.  If the index is out of range for +     * this ringbuffer, [0, size), the behavior for this is undefined. +     */ +    T& operator[](size_t index); + +    /** +     * Return a const reference to the element at a given index.  If the index is out of range +     * for this ringbuffer, [0, size), the behavior for this is undefined. +     */ +    const T& operator[](size_t index) const; + +    /** +     * Return the current size of this RingBuffer. +     */ +    size_t size() const; + +    /** +     * Remove all elements from this RingBuffer and set the size to 0. +     */ +    void clear(); + +private: +    size_t mFrontIdx; +    size_t mMaxBufferSize; +    std::vector<T> mBuffer; +}; // class RingBuffer + + +template <class T> +RingBuffer<T>::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {} + +template <class T> +RingBuffer<T>::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) : +        mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template <class T> +typename RingBuffer<T>::iterator& RingBuffer<T>::iterator::operator++() { +    ++mCtr; + +    if (CC_UNLIKELY(mCtr == mSize)) { +        mPos = mSize; +        return *this; +    } + +    mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); +    return *this; +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::iterator::operator++(int) { +    iterator tmp{mPtr, mSize, mPos, mCtr}; +    ++(*this); +    return tmp; +} + +template <class T> +bool RingBuffer<T>::iterator::operator==(const iterator& rhs) { +    return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template <class T> +bool RingBuffer<T>::iterator::operator!=(const iterator& rhs) { +    return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template <class T> +T& RingBuffer<T>::iterator::operator*() { +    return *(mPtr + mPos); +} + +template <class T> +T* RingBuffer<T>::iterator::operator->() { +    return mPtr + mPos; +} + +template <class T> +RingBuffer<T>::const_iterator::const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr) : +        mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template <class T> +typename RingBuffer<T>::const_iterator& RingBuffer<T>::const_iterator::operator++() { +    ++mCtr; + +    if (CC_UNLIKELY(mCtr == mSize)) { +        mPos = mSize; +        return *this; +    } + +    mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); +    return *this; +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::const_iterator::operator++(int) { +    const_iterator tmp{mPtr, mSize, mPos, mCtr}; +    ++(*this); +    return tmp; +} + +template <class T> +bool RingBuffer<T>::const_iterator::operator==(const const_iterator& rhs) { +    return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template <class T> +bool RingBuffer<T>::const_iterator::operator!=(const const_iterator& rhs) { +    return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template <class T> +const T& RingBuffer<T>::const_iterator::operator*() { +    return *(mPtr + mPos); +} + +template <class T> +const T* RingBuffer<T>::const_iterator::operator->() { +    return mPtr + mPos; +} + +template <class T> +void RingBuffer<T>::add(const T& item) { +    if (mBuffer.size() < mMaxBufferSize) { +        mBuffer.push_back(item); +        mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +        return; +    } + +    mBuffer[mFrontIdx] = item; +    mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +void RingBuffer<T>::add(T&& item) { +    if (mBuffer.size() != mMaxBufferSize) { +        mBuffer.push_back(std::forward<T>(item)); +        mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +        return; +    } + +    // Only works for types with move assignment operator +    mBuffer[mFrontIdx] = std::forward<T>(item); +    mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +template <class... Args> +void RingBuffer<T>::emplace(Args&&... args) { +    if (mBuffer.size() != mMaxBufferSize) { +        mBuffer.emplace_back(std::forward<Args>(args)...); +        mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +        return; +    } + +    // Only works for types with move assignment operator +    mBuffer[mFrontIdx] = T(std::forward<Args>(args)...); +    mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::begin() { +    size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; +    return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::end() { +    size_t s = mBuffer.size(); +    return iterator(mBuffer.data(), s, s, s); +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::begin() const { +    size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; +    return const_iterator(mBuffer.data(), mBuffer.size(), +            (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::end() const { +    size_t s = mBuffer.size(); +    return const_iterator(mBuffer.data(), s, s, s); +} + +template <class T> +T& RingBuffer<T>::operator[](size_t index) { +    LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", +            index, mBuffer.size()); +    size_t pos = (index >= mFrontIdx) ? +            mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; +    return mBuffer[pos]; +} + +template <class T> +const T& RingBuffer<T>::operator[](size_t index) const { +    LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", +            index, mBuffer.size()); +    size_t pos = (index >= mFrontIdx) ? +            mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; +    return mBuffer[pos]; +} + +template <class T> +size_t RingBuffer<T>::size() const { +    return mBuffer.size(); +} + +template <class T> +void RingBuffer<T>::clear() { +    mBuffer.clear(); +    mFrontIdx = 0; +} + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H + +  | 
