diff options
Diffstat (limited to 'services')
17 files changed, 1016 insertions, 702 deletions
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 902d5e4..45df6a9 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -157,8 +157,9 @@ private: bool mFlushHwPending; // track requests for thread flush // for last call to getTimestamp - bool mPreviousValid; - uint32_t mPreviousFramesWritten; + bool mPreviousTimestampValid; + // This is either the first timestamp or one that has passed + // the check to prevent retrograde motion. AudioTimestamp mPreviousTimestamp; }; // end of Track diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 9881764..48bb21f 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5208,7 +5208,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, } if (initFastCapture) { - // create a Pipe for FastMixer to write to, and for us and fast tracks to read from + // create a Pipe for FastCapture to write to, and for us and fast tracks to read from NBAIO_Format format = mInputSource->format(); size_t pipeFramesP2 = roundup(mSampleRate / 25); // double-buffering of 20 ms each size_t pipeSize = pipeFramesP2 * Format_frameSize(format); @@ -5867,8 +5867,9 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe // client expresses a preference for FAST, but we get the final say if (*flags & IAudioFlinger::TRACK_FAST) { if ( - // use case: callback handler - (tid != -1) && + // we formerly checked for a callback handler (non-0 tid), + // but that is no longer required for TRANSFER_OBTAIN mode + // // frame count is not specified, or is exactly the pipe depth ((frameCount == 0) || (frameCount == mPipeFramesP2)) && // PCM data diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 38667b9..7692315 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -405,9 +405,7 @@ AudioFlinger::PlaybackThread::Track::Track( mAudioTrackServerProxy(NULL), mResumeToStopping(false), mFlushHwPending(false), - mPreviousValid(false), - mPreviousFramesWritten(0) - // mPreviousTimestamp + mPreviousTimestampValid(false) { // client == 0 implies sharedBuffer == 0 ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); @@ -864,7 +862,7 @@ void AudioFlinger::PlaybackThread::Track::reset() if (mState == FLUSHED) { mState = IDLE; } - mPreviousValid = false; + mPreviousTimestampValid = false; } } @@ -886,19 +884,22 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times { // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant if (isFastTrack()) { - // FIXME no lock held to set mPreviousValid = false + // FIXME no lock held to set mPreviousTimestampValid = false return INVALID_OPERATION; } sp<ThreadBase> thread = mThread.promote(); if (thread == 0) { - // FIXME no lock held to set mPreviousValid = false + // FIXME no lock held to set mPreviousTimestampValid = false return INVALID_OPERATION; } + Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + + status_t result = INVALID_OPERATION; if (!isOffloaded() && !isDirect()) { if (!playbackThread->mLatchQValid) { - mPreviousValid = false; + mPreviousTimestampValid = false; return INVALID_OPERATION; } uint32_t unpresentedFrames = @@ -914,36 +915,54 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times uint32_t framesWritten = i >= 0 ? playbackThread->mLatchQ.mFramesReleased[i] : mAudioTrackServerProxy->framesReleased(); - bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten; if (framesWritten < unpresentedFrames) { - mPreviousValid = false; - return INVALID_OPERATION; + mPreviousTimestampValid = false; + // return invalid result + } else { + timestamp.mPosition = framesWritten - unpresentedFrames; + timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime; + result = NO_ERROR; } - mPreviousFramesWritten = framesWritten; - uint32_t position = framesWritten - unpresentedFrames; - struct timespec time = playbackThread->mLatchQ.mTimestamp.mTime; - if (checkPreviousTimestamp) { - if (time.tv_sec < mPreviousTimestamp.mTime.tv_sec || - (time.tv_sec == mPreviousTimestamp.mTime.tv_sec && - time.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) { - ALOGW("Time is going backwards"); + } else { // offloaded or direct + result = playbackThread->getTimestamp_l(timestamp); + } + + // Prevent retrograde motion in timestamp. + if (result == NO_ERROR) { + if (mPreviousTimestampValid) { + if (timestamp.mTime.tv_sec < mPreviousTimestamp.mTime.tv_sec || + (timestamp.mTime.tv_sec == mPreviousTimestamp.mTime.tv_sec && + timestamp.mTime.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) { + ALOGW("WARNING - retrograde timestamp time"); + // FIXME Consider blocking this from propagating upwards. } + + // Looking at signed delta will work even when the timestamps + // are wrapping around. + int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition + - mPreviousTimestamp.mPosition); // position can bobble slightly as an artifact; this hides the bobble - static const uint32_t MINIMUM_POSITION_DELTA = 8u; - if ((position <= mPreviousTimestamp.mPosition) || - (position - mPreviousTimestamp.mPosition) < MINIMUM_POSITION_DELTA) { - position = mPreviousTimestamp.mPosition; - time = mPreviousTimestamp.mTime; + static const int32_t MINIMUM_POSITION_DELTA = 8; + if (deltaPosition < 0) { +#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec) + ALOGW("WARNING - retrograde timestamp position corrected," + " %d = %u - %u, (at %llu, %llu nanos)", + deltaPosition, + timestamp.mPosition, + mPreviousTimestamp.mPosition, + TIME_TO_NANOS(timestamp.mTime), + TIME_TO_NANOS(mPreviousTimestamp.mTime)); +#undef TIME_TO_NANOS + } + if (deltaPosition < MINIMUM_POSITION_DELTA) { + // Current timestamp is bad. Use last valid timestamp. + timestamp = mPreviousTimestamp; } } - timestamp.mPosition = position; - timestamp.mTime = time; mPreviousTimestamp = timestamp; - mPreviousValid = true; - return NO_ERROR; + mPreviousTimestampValid = true; } - - return playbackThread->getTimestamp_l(timestamp); + return result; } status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId) diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index eb9116d..00f188f 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -532,7 +532,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() mLock.unlock(); svc.clear(); mLock.lock(); - if (!exitPending() && mAudioCommands.isEmpty()) { + if (!exitPending() && (mAudioCommands.isEmpty() || waitTime != INT64_MAX)) { // release delayed commands wake lock release_wake_lock(mName.string()); ALOGV("AudioCommandThread() going to sleep"); diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index de841c8..9c60911 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -42,7 +42,6 @@ LOCAL_SRC_FILES:= \ api1/client2/CaptureSequencer.cpp \ api1/client2/ZslProcessor3.cpp \ api2/CameraDeviceClient.cpp \ - api_pro/ProCamera2Client.cpp \ device2/Camera2Device.cpp \ device3/Camera3Device.cpp \ device3/Camera3Stream.cpp \ diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 9632ea9..c2df489 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -52,7 +52,6 @@ #include "CameraService.h" #include "api1/CameraClient.h" #include "api1/Camera2Client.h" -#include "api_pro/ProCamera2Client.h" #include "api2/CameraDeviceClient.h" #include "utils/CameraTraces.h" #include "CameraDeviceFactory.h" @@ -1065,16 +1064,6 @@ status_t CameraService::connectLegacy( return NO_ERROR; } -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; -} - status_t CameraService::connectDevice( const sp<ICameraDeviceCallbacks>& cameraCb, int cameraId, @@ -1427,7 +1416,6 @@ status_t CameraService::onTransact( // Permission checks switch (code) { case BnCameraService::CONNECT: - case BnCameraService::CONNECT_PRO: case BnCameraService::CONNECT_DEVICE: case BnCameraService::CONNECT_LEGACY: const int pid = getCallingPid(); @@ -1709,33 +1697,6 @@ void CameraService::Client::OpsCallback::opChanged(int32_t op, } // ---------------------------------------------------------------------------- -// IProCamera -// ---------------------------------------------------------------------------- - -CameraService::ProClient::ProClient(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid) - : CameraService::BasicClient(cameraService, IInterface::asBinder(remoteCallback), - clientPackageName, cameraId, cameraFacing, - clientPid, clientUid, servicePid) -{ - mRemoteCallback = remoteCallback; -} - -CameraService::ProClient::~ProClient() { -} - -void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, - const CaptureResultExtras& resultExtras) { - mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); -} - -// ---------------------------------------------------------------------------- // CameraState // ---------------------------------------------------------------------------- diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 53420e5..53f1c72 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -27,8 +27,6 @@ #include <camera/ICamera.h> #include <camera/ICameraClient.h> -#include <camera/IProCameraUser.h> -#include <camera/IProCameraCallbacks.h> #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> #include <camera/VendorTagDescriptor.h> @@ -127,11 +125,6 @@ public: /*out*/ sp<ICamera>& device); - virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, - int cameraId, const String16& clientPackageName, int clientUid, - /*out*/ - sp<IProCameraUser>& device); - virtual status_t connectDevice( const sp<ICameraDeviceCallbacks>& cameraCb, int cameraId, @@ -195,7 +188,7 @@ public: // virtual inheritance virtual sp<IBinder> asBinderWrapper() = 0; - // Return the remote callback binder object (e.g. IProCameraCallbacks) + // Return the remote callback binder object (e.g. ICameraDeviceCallbacks) sp<IBinder> getRemote() { return mRemoteBinder; } @@ -330,51 +323,6 @@ public: }; // class Client - class ProClient : public BnProCameraUser, public BasicClient { - public: - typedef IProCameraCallbacks TCamCallbacks; - - ProClient(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid); - - virtual ~ProClient(); - - const sp<IProCameraCallbacks>& getRemoteCallback() { - return mRemoteCallback; - } - - /*** - IProCamera implementation - ***/ - virtual status_t connect(const sp<IProCameraCallbacks>& callbacks) - = 0; - virtual status_t exclusiveTryLock() = 0; - virtual status_t exclusiveLock() = 0; - virtual status_t exclusiveUnlock() = 0; - - virtual bool hasExclusiveLock() = 0; - - // Note that the callee gets a copy of the metadata. - virtual int submitRequest(camera_metadata_t* metadata, - bool streaming = false) = 0; - virtual status_t cancelRequest(int requestId) = 0; - - // Callbacks from camera service - virtual void onExclusiveLockStolen() = 0; - - 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; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 0e2311c..8587e0e 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -645,9 +645,6 @@ void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras, } } -// TODO: refactor the code below this with IProCameraUser. -// it's 100% copy-pasted, so lets not change it right now to make it easier. - void CameraDeviceClient::detachDevice() { if (mDevice == 0) return; diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp deleted file mode 100644 index a977494..0000000 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#define LOG_TAG "ProCamera2Client" -#define ATRACE_TAG ATRACE_TAG_CAMERA -//#define LOG_NDEBUG 0 - -#include <utils/Log.h> -#include <utils/Trace.h> - -#include <cutils/properties.h> -#include <gui/Surface.h> -#include <gui/Surface.h> - -#include "api_pro/ProCamera2Client.h" -#include "common/CameraDeviceBase.h" - -namespace android { -using namespace camera2; - -// Interface used by CameraService - -ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid) : - Camera2ClientBase(cameraService, remoteCallback, clientPackageName, - cameraId, cameraFacing, clientPid, clientUid, servicePid) -{ - ATRACE_CALL(); - ALOGI("ProCamera %d: Opened", cameraId); - - mExclusiveLock = false; -} - -status_t ProCamera2Client::initialize(CameraModule *module) -{ - ATRACE_CALL(); - status_t res; - - res = Camera2ClientBase::initialize(module); - if (res != OK) { - return res; - } - - String8 threadName; - mFrameProcessor = new FrameProcessorBase(mDevice); - threadName = String8::format("PC2-%d-FrameProc", mCameraId); - mFrameProcessor->run(threadName.string()); - - mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID, - FRAME_PROCESSOR_LISTENER_MAX_ID, - /*listener*/this); - - return OK; -} - -ProCamera2Client::~ProCamera2Client() { -} - -status_t ProCamera2Client::exclusiveTryLock() { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (!mDevice.get()) return PERMISSION_DENIED; - - if (!mExclusiveLock) { - mExclusiveLock = true; - - if (mRemoteCallback != NULL) { - mRemoteCallback->onLockStatusChanged( - IProCameraCallbacks::LOCK_ACQUIRED); - } - - ALOGV("%s: exclusive lock acquired", __FUNCTION__); - - return OK; - } - - // TODO: have a PERMISSION_DENIED case for when someone else owns the lock - - // don't allow recursive locking - ALOGW("%s: exclusive lock already exists - recursive locking is not" - "allowed", __FUNCTION__); - - return ALREADY_EXISTS; -} - -status_t ProCamera2Client::exclusiveLock() { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (!mDevice.get()) return PERMISSION_DENIED; - - /** - * TODO: this should asynchronously 'wait' until the lock becomes available - * if another client already has an exclusive lock. - * - * once we have proper sharing support this will need to do - * more than just return immediately - */ - if (!mExclusiveLock) { - mExclusiveLock = true; - - if (mRemoteCallback != NULL) { - mRemoteCallback->onLockStatusChanged(IProCameraCallbacks::LOCK_ACQUIRED); - } - - ALOGV("%s: exclusive lock acquired", __FUNCTION__); - - return OK; - } - - // don't allow recursive locking - ALOGW("%s: exclusive lock already exists - recursive locking is not allowed" - , __FUNCTION__); - return ALREADY_EXISTS; -} - -status_t ProCamera2Client::exclusiveUnlock() { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - // don't allow unlocking if we have no lock - if (!mExclusiveLock) { - ALOGW("%s: cannot unlock, no lock was held in the first place", - __FUNCTION__); - return BAD_VALUE; - } - - mExclusiveLock = false; - if (mRemoteCallback != NULL ) { - mRemoteCallback->onLockStatusChanged( - IProCameraCallbacks::LOCK_RELEASED); - } - ALOGV("%s: exclusive lock released", __FUNCTION__); - - return OK; -} - -bool ProCamera2Client::hasExclusiveLock() { - Mutex::Autolock icl(mBinderSerializationLock); - return mExclusiveLock; -} - -void ProCamera2Client::onExclusiveLockStolen() { - ALOGV("%s: ProClient lost exclusivity (id %d)", - __FUNCTION__, mCameraId); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (mExclusiveLock && mRemoteCallback.get() != NULL) { - mRemoteCallback->onLockStatusChanged( - IProCameraCallbacks::LOCK_STOLEN); - } - - mExclusiveLock = false; - - //TODO: we should not need to detach the device, merely reset it. - detachDevice(); -} - -status_t ProCamera2Client::submitRequest(camera_metadata_t* request, - bool streaming) { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - if (!mExclusiveLock) { - return PERMISSION_DENIED; - } - - CameraMetadata metadata(request); - - if (!enforceRequestPermissions(metadata)) { - return PERMISSION_DENIED; - } - - if (streaming) { - return mDevice->setStreamingRequest(metadata); - } else { - return mDevice->capture(metadata); - } - - // unreachable. thx gcc for a useless warning - return OK; -} - -status_t ProCamera2Client::cancelRequest(int requestId) { - (void)requestId; - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - if (!mExclusiveLock) { - return PERMISSION_DENIED; - } - - // TODO: implement - ALOGE("%s: not fully implemented yet", __FUNCTION__); - return INVALID_OPERATION; -} - -status_t ProCamera2Client::deleteStream(int streamId) { - ATRACE_CALL(); - ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId); - - status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - mDevice->clearStreamingRequest(); - - status_t code; - if ((code = mDevice->waitUntilDrained()) != OK) { - ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code); - } - - return mDevice->deleteStream(streamId); -} - -status_t ProCamera2Client::createStream(int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer, - /*out*/ - int* streamId) -{ - if (streamId) { - *streamId = -1; - } - - ATRACE_CALL(); - ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format); - - status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - sp<IBinder> binder; - sp<ANativeWindow> window; - if (bufferProducer != 0) { - binder = IInterface::asBinder(bufferProducer); - window = new Surface(bufferProducer); - } - - return mDevice->createStream(window, width, height, format, - HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, - streamId); -} - -// Create a request object from a template. -// -- Caller owns the newly allocated metadata -status_t ProCamera2Client::createDefaultRequest(int templateId, - /*out*/ - camera_metadata** request) -{ - ATRACE_CALL(); - ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId); - - if (request) { - *request = NULL; - } - - status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - CameraMetadata metadata; - if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) { - *request = metadata.release(); - } - - return res; -} - -status_t ProCamera2Client::getCameraInfo(int cameraId, - /*out*/ - camera_metadata** info) -{ - if (cameraId != mCameraId) { - return INVALID_OPERATION; - } - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - CameraMetadata deviceInfo = mDevice->info(); - *info = deviceInfo.release(); - - return OK; -} - -status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) { - String8 result; - result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n", - mCameraId, - (getRemoteCallback() != NULL ? - IInterface::asBinder(getRemoteCallback()).get() : NULL), - mClientPid); - result.append(" State:\n"); - write(fd, result.string(), result.size()); - - // TODO: print dynamic/request section from most recent requests - mFrameProcessor->dump(fd, args); - return dumpDevice(fd, args); -} - -// IProCameraUser interface - -void ProCamera2Client::detachDevice() { - if (mDevice == 0) return; - - ALOGV("Camera %d: Stopping processors", mCameraId); - - mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID, - FRAME_PROCESSOR_LISTENER_MAX_ID, - /*listener*/this); - mFrameProcessor->requestExit(); - ALOGV("Camera %d: Waiting for threads", mCameraId); - mFrameProcessor->join(); - ALOGV("Camera %d: Disconnecting device", mCameraId); - - // WORKAROUND: HAL refuses to disconnect while there's streams in flight - { - mDevice->clearStreamingRequest(); - - status_t code; - if ((code = mDevice->waitUntilDrained()) != OK) { - ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, - code); - } - } - - Camera2ClientBase::detachDevice(); -} - -void ProCamera2Client::onResultAvailable(const CaptureResult& result) { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (mRemoteCallback != NULL) { - CameraMetadata tmp(result.mMetadata); - camera_metadata_t* meta = tmp.release(); - ALOGV("%s: meta = %p ", __FUNCTION__, meta); - mRemoteCallback->onResultReceived(result.mResultExtras.requestId, meta); - tmp.acquire(meta); - } -} - -bool ProCamera2Client::enforceRequestPermissions(CameraMetadata& metadata) { - - const int pid = IPCThreadState::self()->getCallingPid(); - const int selfPid = getpid(); - camera_metadata_entry_t entry; - - /** - * Mixin default important security values - * - android.led.transmit = defaulted ON - */ - CameraMetadata staticInfo = mDevice->info(); - entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS); - for(size_t i = 0; i < entry.count; ++i) { - uint8_t led = entry.data.u8[i]; - - switch(led) { - case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: { - uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON; - if (!metadata.exists(ANDROID_LED_TRANSMIT)) { - metadata.update(ANDROID_LED_TRANSMIT, - &transmitDefault, 1); - } - break; - } - } - } - - // We can do anything! - if (pid == selfPid) { - return true; - } - - /** - * Permission check special fields in the request - * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT - */ - entry = metadata.find(ANDROID_LED_TRANSMIT); - if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) { - String16 permissionString = - String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED"); - if (!checkCallingPermission(permissionString)) { - const int uid = IPCThreadState::self()->getCallingUid(); - ALOGE("Permission Denial: " - "can't disable transmit LED pid=%d, uid=%d", pid, uid); - return false; - } - } - - return true; -} - -} // namespace android diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h deleted file mode 100644 index 7f5f6ac..0000000 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2013 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_SERVERS_CAMERA_PROCAMERA2CLIENT_H -#define ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H - -#include "CameraService.h" -#include "common/FrameProcessorBase.h" -#include "common/Camera2ClientBase.h" -#include "device2/Camera2Device.h" -#include "camera/CaptureResult.h" - -namespace android { - -class IMemory; -/** - * Implements the binder IProCameraUser API, - * meant for HAL2-level private API access. - */ -class ProCamera2Client : - public Camera2ClientBase<CameraService::ProClient>, - public camera2::FrameProcessorBase::FilteredListener -{ -public: - /** - * IProCameraUser interface (see IProCameraUser for details) - */ - virtual status_t exclusiveTryLock(); - virtual status_t exclusiveLock(); - virtual status_t exclusiveUnlock(); - - virtual bool hasExclusiveLock(); - - // Note that the callee gets a copy of the metadata. - virtual int submitRequest(camera_metadata_t* metadata, - bool streaming = false); - virtual status_t cancelRequest(int requestId); - - virtual status_t deleteStream(int streamId); - - virtual status_t createStream( - int width, - int height, - int format, - const sp<IGraphicBufferProducer>& bufferProducer, - /*out*/ - int* streamId); - - // Create a request object from a template. - // -- Caller owns the newly allocated metadata - virtual status_t createDefaultRequest(int templateId, - /*out*/ - camera_metadata** request); - - // Get the static metadata for the camera - // -- Caller owns the newly allocated metadata - virtual status_t getCameraInfo(int cameraId, - /*out*/ - camera_metadata** info); - - /** - * Interface used by CameraService - */ - - ProCamera2Client(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid); - virtual ~ProCamera2Client(); - - virtual status_t initialize(CameraModule *module); - - virtual status_t dump(int fd, const Vector<String16>& args); - - // Callbacks from camera service - virtual void onExclusiveLockStolen(); - - /** - * Interface used by independent components of ProCamera2Client. - */ - -protected: - /** FilteredListener implementation **/ - virtual void onResultAvailable(const CaptureResult& result); - - virtual void detachDevice(); - -private: - /** IProCameraUser interface-related private members */ - - /** Preview callback related members */ - sp<camera2::FrameProcessorBase> mFrameProcessor; - static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0; - static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL; - - /** Utility members */ - bool enforceRequestPermissions(CameraMetadata& metadata); - - // Whether or not we have an exclusive lock on the device - // - if no we can't modify the request queue. - // note that creating/deleting streams we own is still OK - bool mExclusiveLock; -}; - -}; // namespace android - -#endif diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index 0415d67..c0c2314 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -337,7 +337,6 @@ void Camera2ClientBase<TClientBase>::SharedCameraCallbacks::clear() { mRemoteCallback.clear(); } -template class Camera2ClientBase<CameraService::ProClient>; template class Camera2ClientBase<CameraService::Client>; template class Camera2ClientBase<CameraDeviceClientBase>; diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index eb21d55..168ea0a 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -36,7 +36,7 @@ public: typedef typename TClientBase::TCamCallbacks TCamCallbacks; /** - * Base binder interface (see ICamera/IProCameraUser for details) + * Base binder interface (see ICamera/ICameraDeviceUser for details) */ virtual status_t connect(const sp<TCamCallbacks>& callbacks); virtual void disconnect(); diff --git a/services/mediaresourcemanager/Android.mk b/services/mediaresourcemanager/Android.mk new file mode 100644 index 0000000..84218cf --- /dev/null +++ b/services/mediaresourcemanager/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := ResourceManagerService.cpp + +LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog + +LOCAL_MODULE:= libresourcemanagerservice + +LOCAL_32_BIT_ONLY := true + +LOCAL_C_INCLUDES += \ + $(TOPDIR)frameworks/av/include + +include $(BUILD_SHARED_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp new file mode 100644 index 0000000..7296d47 --- /dev/null +++ b/services/mediaresourcemanager/ResourceManagerService.cpp @@ -0,0 +1,345 @@ +/* +** +** Copyright 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. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ResourceManagerService" +#include <utils/Log.h> + +#include <binder/IServiceManager.h> +#include <dirent.h> +#include <media/stagefright/ProcessInfo.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <unistd.h> + +#include "ResourceManagerService.h" + +namespace android { + +template <typename T> +static String8 getString(const Vector<T> &items) { + String8 itemsStr; + for (size_t i = 0; i < items.size(); ++i) { + itemsStr.appendFormat("%s ", items[i].toString().string()); + } + return itemsStr; +} + +static bool hasResourceType(String8 type, Vector<MediaResource> resources) { + for (size_t i = 0; i < resources.size(); ++i) { + if (resources[i].mType == type) { + return true; + } + } + return false; +} + +static bool hasResourceType(String8 type, ResourceInfos infos) { + for (size_t i = 0; i < infos.size(); ++i) { + if (hasResourceType(type, infos[i].resources)) { + return true; + } + } + return false; +} + +static ResourceInfos& getResourceInfosForEdit( + int pid, + PidResourceInfosMap& map) { + ssize_t index = map.indexOfKey(pid); + if (index < 0) { + // new pid + ResourceInfos infosForPid; + map.add(pid, infosForPid); + } + + return map.editValueFor(pid); +} + +static ResourceInfo& getResourceInfoForEdit( + int64_t clientId, + const sp<IResourceManagerClient> client, + ResourceInfos& infos) { + for (size_t i = 0; i < infos.size(); ++i) { + if (infos[i].clientId == clientId) { + return infos.editItemAt(i); + } + } + ResourceInfo info; + info.clientId = clientId; + info.client = client; + infos.push_back(info); + return infos.editItemAt(infos.size() - 1); +} + +ResourceManagerService::ResourceManagerService() + : mProcessInfo(new ProcessInfo()), + mSupportsMultipleSecureCodecs(true), + mSupportsSecureWithNonSecureCodec(true) {} + +ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo) + : mProcessInfo(processInfo), + mSupportsMultipleSecureCodecs(true), + mSupportsSecureWithNonSecureCodec(true) {} + +ResourceManagerService::~ResourceManagerService() {} + +void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) { + ALOGV("config(%s)", getString(policies).string()); + + Mutex::Autolock lock(mLock); + for (size_t i = 0; i < policies.size(); ++i) { + String8 type = policies[i].mType; + uint64_t value = policies[i].mValue; + if (type == kPolicySupportsMultipleSecureCodecs) { + mSupportsMultipleSecureCodecs = (value != 0); + } else if (type == kPolicySupportsSecureWithNonSecureCodec) { + mSupportsSecureWithNonSecureCodec = (value != 0); + } + } +} + +void ResourceManagerService::addResource( + int pid, + int64_t clientId, + const sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources) { + ALOGV("addResource(pid %d, clientId %lld, resources %s)", + pid, (long long) clientId, getString(resources).string()); + + Mutex::Autolock lock(mLock); + ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); + ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos); + info.resources.appendVector(resources); +} + +void ResourceManagerService::removeResource(int64_t clientId) { + ALOGV("removeResource(%lld)", (long long) clientId); + + Mutex::Autolock lock(mLock); + bool found = false; + for (size_t i = 0; i < mMap.size(); ++i) { + ResourceInfos &infos = mMap.editValueAt(i); + for (size_t j = 0; j < infos.size();) { + if (infos[j].clientId == clientId) { + j = infos.removeAt(j); + found = true; + } else { + ++j; + } + } + if (found) { + break; + } + } + if (!found) { + ALOGV("didn't find client"); + } +} + +bool ResourceManagerService::reclaimResource( + int callingPid, const Vector<MediaResource> &resources) { + ALOGV("reclaimResource(callingPid %d, resources %s)", + callingPid, getString(resources).string()); + + Vector<sp<IResourceManagerClient>> clients; + { + Mutex::Autolock lock(mLock); + // first pass to handle secure/non-secure codec conflict + for (size_t i = 0; i < resources.size(); ++i) { + String8 type = resources[i].mType; + if (type == kResourceSecureCodec) { + if (!mSupportsMultipleSecureCodecs) { + if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) { + return false; + } + } + if (!mSupportsSecureWithNonSecureCodec) { + if (!getAllClients_l(callingPid, String8(kResourceNonSecureCodec), &clients)) { + return false; + } + } + } else if (type == kResourceNonSecureCodec) { + if (!mSupportsSecureWithNonSecureCodec) { + if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) { + return false; + } + } + } + } + + if (clients.size() == 0) { + // if no secure/non-secure codec conflict, run second pass to handle other resources. + for (size_t i = 0; i < resources.size(); ++i) { + String8 type = resources[i].mType; + if (type == kResourceGraphicMemory) { + sp<IResourceManagerClient> client; + if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) { + return false; + } + clients.push_back(client); + } + } + } + } + + if (clients.size() == 0) { + return false; + } + + for (size_t i = 0; i < clients.size(); ++i) { + ALOGV("reclaimResource from client %p", clients[i].get()); + if (!clients[i]->reclaimResource()) { + return false; + } + } + return true; +} + +bool ResourceManagerService::getAllClients_l( + int callingPid, const String8 &type, Vector<sp<IResourceManagerClient>> *clients) { + Vector<sp<IResourceManagerClient>> temp; + for (size_t i = 0; i < mMap.size(); ++i) { + ResourceInfos &infos = mMap.editValueAt(i); + for (size_t j = 0; j < infos.size(); ++j) { + if (hasResourceType(type, infos[j].resources)) { + if (!isCallingPriorityHigher_l(callingPid, mMap.keyAt(i))) { + // some higher/equal priority process owns the resource, + // this request can't be fulfilled. + ALOGE("getAllClients_l: can't reclaim resource %s from pid %d", + type.string(), mMap.keyAt(i)); + return false; + } + temp.push_back(infos[j].client); + } + } + } + if (temp.size() == 0) { + ALOGV("getAllClients_l: didn't find any resource %s", type.string()); + return true; + } + clients->appendVector(temp); + return true; +} + +bool ResourceManagerService::getLowestPriorityBiggestClient_l( + int callingPid, const String8 &type, sp<IResourceManagerClient> *client) { + int lowestPriorityPid; + int lowestPriority; + int callingPriority; + if (!mProcessInfo->getPriority(callingPid, &callingPriority)) { + ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d", + callingPid); + return false; + } + if (!getLowestPriorityPid_l(type, &lowestPriorityPid, &lowestPriority)) { + return false; + } + if (lowestPriority <= callingPriority) { + ALOGE("getLowestPriorityBiggestClient_l: lowest priority %d vs caller priority %d", + lowestPriority, callingPriority); + return false; + } + + if (!getBiggestClient_l(lowestPriorityPid, type, client)) { + return false; + } + return true; +} + +bool ResourceManagerService::getLowestPriorityPid_l( + const String8 &type, int *lowestPriorityPid, int *lowestPriority) { + int pid = -1; + int priority = -1; + for (size_t i = 0; i < mMap.size(); ++i) { + if (mMap.valueAt(i).size() == 0) { + // no client on this process. + continue; + } + if (!hasResourceType(type, mMap.valueAt(i))) { + // doesn't have the requested resource type + continue; + } + int tempPid = mMap.keyAt(i); + int tempPriority; + if (!mProcessInfo->getPriority(tempPid, &tempPriority)) { + ALOGV("getLowestPriorityPid_l: can't get priority of pid %d, skipped", tempPid); + // TODO: remove this pid from mMap? + continue; + } + if (pid == -1 || tempPriority > priority) { + // initial the value + pid = tempPid; + priority = tempPriority; + } + } + if (pid != -1) { + *lowestPriorityPid = pid; + *lowestPriority = priority; + } + return (pid != -1); +} + +bool ResourceManagerService::isCallingPriorityHigher_l(int callingPid, int pid) { + int callingPidPriority; + if (!mProcessInfo->getPriority(callingPid, &callingPidPriority)) { + return false; + } + + int priority; + if (!mProcessInfo->getPriority(pid, &priority)) { + return false; + } + + return (callingPidPriority < priority); +} + +bool ResourceManagerService::getBiggestClient_l( + int pid, const String8 &type, sp<IResourceManagerClient> *client) { + ssize_t index = mMap.indexOfKey(pid); + if (index < 0) { + ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid); + return false; + } + + sp<IResourceManagerClient> clientTemp; + uint64_t largestValue = 0; + const ResourceInfos &infos = mMap.valueAt(index); + for (size_t i = 0; i < infos.size(); ++i) { + Vector<MediaResource> resources = infos[i].resources; + for (size_t j = 0; j < resources.size(); ++j) { + if (resources[j].mType == type) { + if (resources[j].mValue > largestValue) { + largestValue = resources[j].mValue; + clientTemp = infos[i].client; + } + } + } + } + + if (clientTemp == NULL) { + ALOGE("getBiggestClient_l: can't find resource type %s for pid %d", type.string(), pid); + return false; + } + + *client = clientTemp; + return true; +} + +} // namespace android diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h new file mode 100644 index 0000000..2ed9bf8 --- /dev/null +++ b/services/mediaresourcemanager/ResourceManagerService.h @@ -0,0 +1,106 @@ +/* +** +** Copyright 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_RESOURCEMANAGERSERVICE_H +#define ANDROID_RESOURCEMANAGERSERVICE_H + +#include <arpa/inet.h> +#include <binder/BinderService.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/threads.h> +#include <utils/Vector.h> + +#include <media/IResourceManagerService.h> + +namespace android { + +struct ProcessInfoInterface; + +struct ResourceInfo { + int64_t clientId; + sp<IResourceManagerClient> client; + Vector<MediaResource> resources; +}; + +typedef Vector<ResourceInfo> ResourceInfos; +typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap; + +class ResourceManagerService + : public BinderService<ResourceManagerService>, + public BnResourceManagerService +{ +public: + static char const *getServiceName() { return "media.resource_manager"; } + + ResourceManagerService(); + ResourceManagerService(sp<ProcessInfoInterface> processInfo); + + // IResourceManagerService interface + virtual void config(const Vector<MediaResourcePolicy> &policies); + + virtual void addResource( + int pid, + int64_t clientId, + const sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources); + + virtual void removeResource(int64_t clientId); + + virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources); + +protected: + virtual ~ResourceManagerService(); + +private: + friend class ResourceManagerServiceTest; + + // Gets the list of all the clients who own the specified resource type. + // Returns false if any client belongs to a process with higher priority than the + // calling process. The clients will remain unchanged if returns false. + bool getAllClients_l(int callingPid, const String8 &type, + Vector<sp<IResourceManagerClient>> *clients); + + // Gets the client who owns specified resource type from lowest possible priority process. + // Returns false if the calling process priority is not higher than the lowest process + // priority. The client will remain unchanged if returns false. + bool getLowestPriorityBiggestClient_l(int callingPid, const String8 &type, + sp<IResourceManagerClient> *client); + + // Gets lowest priority process that has the specified resource type. + // Returns false if failed. The output parameters will remain unchanged if failed. + bool getLowestPriorityPid_l(const String8 &type, int *pid, int *priority); + + // Gets the client who owns biggest piece of specified resource type from pid. + // Returns false if failed. The client will remain unchanged if failed. + bool getBiggestClient_l(int pid, const String8 &type, sp<IResourceManagerClient> *client); + + bool isCallingPriorityHigher_l(int callingPid, int pid); + + mutable Mutex mLock; + sp<ProcessInfoInterface> mProcessInfo; + PidResourceInfosMap mMap; + bool mSupportsMultipleSecureCodecs; + bool mSupportsSecureWithNonSecureCodec; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_RESOURCEMANAGERSERVICE_H diff --git a/services/mediaresourcemanager/test/Android.mk b/services/mediaresourcemanager/test/Android.mk new file mode 100644 index 0000000..228b62a --- /dev/null +++ b/services/mediaresourcemanager/test/Android.mk @@ -0,0 +1,25 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := ResourceManagerService_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + ResourceManagerService_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libmedia \ + libresourcemanagerservice \ + libutils \ + +LOCAL_C_INCLUDES := \ + frameworks/av/include \ + frameworks/av/services/mediaresourcemanager \ + +LOCAL_32_BIT_ONLY := true + +include $(BUILD_NATIVE_TEST) diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp new file mode 100644 index 0000000..b73e1bc --- /dev/null +++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp @@ -0,0 +1,464 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ResourceManagerService_test" +#include <utils/Log.h> + +#include <gtest/gtest.h> + +#include "ResourceManagerService.h" +#include <media/IResourceManagerService.h> +#include <media/MediaResource.h> +#include <media/MediaResourcePolicy.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/ProcessInfoInterface.h> + +namespace android { + +struct TestProcessInfo : public ProcessInfoInterface { + TestProcessInfo() {} + virtual ~TestProcessInfo() {} + + virtual bool getPriority(int pid, int *priority) { + // For testing, use pid as priority. + // Lower the value higher the priority. + *priority = pid; + return true; + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo); +}; + +struct TestClient : public BnResourceManagerClient { + TestClient(sp<ResourceManagerService> service) + : mReclaimed(false), mService(service) {} + + virtual bool reclaimResource() { + sp<IResourceManagerClient> client(this); + mService->removeResource((int64_t) client.get()); + mReclaimed = true; + return true; + } + + bool reclaimed() const { + return mReclaimed; + } + + void reset() { + mReclaimed = false; + } + +protected: + virtual ~TestClient() {} + +private: + bool mReclaimed; + sp<ResourceManagerService> mService; + DISALLOW_EVIL_CONSTRUCTORS(TestClient); +}; + +static const int kTestPid1 = 30; +static const int kTestPid2 = 20; + +class ResourceManagerServiceTest : public ::testing::Test { +public: + ResourceManagerServiceTest() + : mService(new ResourceManagerService(new TestProcessInfo)), + mTestClient1(new TestClient(mService)), + mTestClient2(new TestClient(mService)), + mTestClient3(new TestClient(mService)) { + } + +protected: + static bool isEqualResources(const Vector<MediaResource> &resources1, + const Vector<MediaResource> &resources2) { + if (resources1.size() != resources2.size()) { + return false; + } + for (size_t i = 0; i < resources1.size(); ++i) { + if (resources1[i] != resources2[i]) { + return false; + } + } + return true; + } + + static void expectEqResourceInfo(const ResourceInfo &info, sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources) { + EXPECT_EQ(client, info.client); + EXPECT_TRUE(isEqualResources(resources, info.resources)); + } + + void verifyClients(bool c1, bool c2, bool c3) { + TestClient *client1 = static_cast<TestClient*>(mTestClient1.get()); + TestClient *client2 = static_cast<TestClient*>(mTestClient2.get()); + TestClient *client3 = static_cast<TestClient*>(mTestClient3.get()); + + EXPECT_EQ(c1, client1->reclaimed()); + EXPECT_EQ(c2, client2->reclaimed()); + EXPECT_EQ(c3, client3->reclaimed()); + + client1->reset(); + client2->reset(); + client3->reset(); + } + + void addResource() { + // kTestPid1 mTestClient1 + Vector<MediaResource> resources1; + resources1.push_back(MediaResource(String8(kResourceSecureCodec), 1)); + mService->addResource(kTestPid1, (int64_t) mTestClient1.get(), mTestClient1, resources1); + resources1.push_back(MediaResource(String8(kResourceGraphicMemory), 200)); + Vector<MediaResource> resources11; + resources11.push_back(MediaResource(String8(kResourceGraphicMemory), 200)); + mService->addResource(kTestPid1, (int64_t) mTestClient1.get(), mTestClient1, resources11); + + // kTestPid2 mTestClient2 + Vector<MediaResource> resources2; + resources2.push_back(MediaResource(String8(kResourceNonSecureCodec), 1)); + resources2.push_back(MediaResource(String8(kResourceGraphicMemory), 300)); + mService->addResource(kTestPid2, (int64_t) mTestClient2.get(), mTestClient2, resources2); + + // kTestPid2 mTestClient3 + Vector<MediaResource> resources3; + mService->addResource(kTestPid2, (int64_t) mTestClient3.get(), mTestClient3, resources3); + resources3.push_back(MediaResource(String8(kResourceSecureCodec), 1)); + resources3.push_back(MediaResource(String8(kResourceGraphicMemory), 100)); + mService->addResource(kTestPid2, (int64_t) mTestClient3.get(), mTestClient3, resources3); + + const PidResourceInfosMap &map = mService->mMap; + EXPECT_EQ(2u, map.size()); + ssize_t index1 = map.indexOfKey(kTestPid1); + ASSERT_GE(index1, 0); + const ResourceInfos &infos1 = map[index1]; + EXPECT_EQ(1u, infos1.size()); + expectEqResourceInfo(infos1[0], mTestClient1, resources1); + + ssize_t index2 = map.indexOfKey(kTestPid2); + ASSERT_GE(index2, 0); + const ResourceInfos &infos2 = map[index2]; + EXPECT_EQ(2u, infos2.size()); + expectEqResourceInfo(infos2[0], mTestClient2, resources2); + expectEqResourceInfo(infos2[1], mTestClient3, resources3); + } + + void testConfig() { + EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs); + EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec); + + Vector<MediaResourcePolicy> policies1; + policies1.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 1)); + policies1.push_back( + MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 0)); + mService->config(policies1); + EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs); + EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec); + + Vector<MediaResourcePolicy> policies2; + policies2.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 0)); + policies2.push_back( + MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 1)); + mService->config(policies2); + EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs); + EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec); + } + + void testRemoveResource() { + addResource(); + + mService->removeResource((int64_t) mTestClient2.get()); + + const PidResourceInfosMap &map = mService->mMap; + EXPECT_EQ(2u, map.size()); + const ResourceInfos &infos1 = map.valueFor(kTestPid1); + const ResourceInfos &infos2 = map.valueFor(kTestPid2); + EXPECT_EQ(1u, infos1.size()); + EXPECT_EQ(1u, infos2.size()); + // mTestClient2 has been removed. + EXPECT_EQ(mTestClient3, infos2[0].client); + } + + void testGetAllClients() { + addResource(); + + String8 type = String8(kResourceSecureCodec); + String8 unknowType = String8("unknowType"); + Vector<sp<IResourceManagerClient> > clients; + int lowPriorityPid = 100; + EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); + int midPriorityPid = 25; + EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); + int highPriorityPid = 10; + EXPECT_TRUE(mService->getAllClients_l(10, unknowType, &clients)); + EXPECT_TRUE(mService->getAllClients_l(10, type, &clients)); + + EXPECT_EQ(2u, clients.size()); + EXPECT_EQ(mTestClient3, clients[0]); + EXPECT_EQ(mTestClient1, clients[1]); + } + + void testReclaimResourceSecure() { + Vector<MediaResource> resources; + resources.push_back(MediaResource(String8(kResourceSecureCodec), 1)); + resources.push_back(MediaResource(String8(kResourceGraphicMemory), 150)); + + // ### secure codec can't coexist and secure codec can coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = false; + mService->mSupportsSecureWithNonSecureCodec = true; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, false, true); + + // call again should reclaim one largest graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + // ### secure codecs can't coexist and secure codec can't coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = false; + mService->mSupportsSecureWithNonSecureCodec = false; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all secure and non-secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, true, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + + // ### secure codecs can coexist but secure codec can't coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = true; + mService->mSupportsSecureWithNonSecureCodec = false; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all non-secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // call again should reclaim one largest graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, false, false); + + // call again should reclaim another largest graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, false, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + // ### secure codecs can coexist and secure codec can coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = true; + mService->mSupportsSecureWithNonSecureCodec = true; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + + EXPECT_TRUE(mService->reclaimResource(10, resources)); + // one largest graphic memory from lowest process got reclaimed + verifyClients(true, false, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, false, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + } + + void testReclaimResourceNonSecure() { + Vector<MediaResource> resources; + resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1)); + resources.push_back(MediaResource(String8(kResourceGraphicMemory), 150)); + + // ### secure codec can't coexist with non-secure codec ### + { + addResource(); + mService->mSupportsSecureWithNonSecureCodec = false; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, false, true); + + // call again should reclaim one graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + + // ### secure codec can coexist with non-secure codec ### + { + addResource(); + mService->mSupportsSecureWithNonSecureCodec = true; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + + EXPECT_TRUE(mService->reclaimResource(10, resources)); + // one largest graphic memory from lowest process got reclaimed + verifyClients(true, false, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, false, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + } + + void testGetLowestPriorityBiggestClient() { + String8 type = String8(kResourceGraphicMemory); + sp<IResourceManagerClient> client; + EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(10, type, &client)); + + addResource(); + + EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(100, type, &client)); + EXPECT_TRUE(mService->getLowestPriorityBiggestClient_l(10, type, &client)); + + // kTestPid1 is the lowest priority process with kResourceGraphicMemory. + // mTestClient1 has the largest kResourceGraphicMemory within kTestPid1. + EXPECT_EQ(mTestClient1, client); + } + + void testGetLowestPriorityPid() { + int pid; + int priority; + TestProcessInfo processInfo; + + String8 type = String8(kResourceGraphicMemory); + EXPECT_FALSE(mService->getLowestPriorityPid_l(type, &pid, &priority)); + + addResource(); + + EXPECT_TRUE(mService->getLowestPriorityPid_l(type, &pid, &priority)); + EXPECT_EQ(kTestPid1, pid); + int priority1; + processInfo.getPriority(kTestPid1, &priority1); + EXPECT_EQ(priority1, priority); + + type = String8(kResourceNonSecureCodec); + EXPECT_TRUE(mService->getLowestPriorityPid_l(type, &pid, &priority)); + EXPECT_EQ(kTestPid2, pid); + int priority2; + processInfo.getPriority(kTestPid2, &priority2); + EXPECT_EQ(priority2, priority); + } + + void testGetBiggestClient() { + String8 type = String8(kResourceGraphicMemory); + sp<IResourceManagerClient> client; + EXPECT_FALSE(mService->getBiggestClient_l(kTestPid2, type, &client)); + + addResource(); + + EXPECT_TRUE(mService->getBiggestClient_l(kTestPid2, type, &client)); + EXPECT_EQ(mTestClient2, client); + } + + void testIsCallingPriorityHigher() { + EXPECT_FALSE(mService->isCallingPriorityHigher_l(101, 100)); + EXPECT_FALSE(mService->isCallingPriorityHigher_l(100, 100)); + EXPECT_TRUE(mService->isCallingPriorityHigher_l(99, 100)); + } + + sp<ResourceManagerService> mService; + sp<IResourceManagerClient> mTestClient1; + sp<IResourceManagerClient> mTestClient2; + sp<IResourceManagerClient> mTestClient3; +}; + +TEST_F(ResourceManagerServiceTest, config) { + testConfig(); +} + +TEST_F(ResourceManagerServiceTest, addResource) { + addResource(); +} + +TEST_F(ResourceManagerServiceTest, removeResource) { + testRemoveResource(); +} + +TEST_F(ResourceManagerServiceTest, reclaimResource) { + testReclaimResourceSecure(); + testReclaimResourceNonSecure(); +} + +TEST_F(ResourceManagerServiceTest, getAllClients_l) { + testGetAllClients(); +} + +TEST_F(ResourceManagerServiceTest, getLowestPriorityBiggestClient_l) { + testGetLowestPriorityBiggestClient(); +} + +TEST_F(ResourceManagerServiceTest, getLowestPriorityPid_l) { + testGetLowestPriorityPid(); +} + +TEST_F(ResourceManagerServiceTest, getBiggestClient_l) { + testGetBiggestClient(); +} + +TEST_F(ResourceManagerServiceTest, isCallingPriorityHigher_l) { + testIsCallingPriorityHigher(); +} + +} // namespace android |