diff options
Diffstat (limited to 'services')
20 files changed, 346 insertions, 223 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 3e4b1fc..bd6889d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1421,6 +1421,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, + const String16& opPackageName, size_t *frameCount, IAudioFlinger::track_flags_t *flags, pid_t tid, @@ -1440,7 +1441,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( buffers.clear(); // check calling permissions - if (!recordingAllowed()) { + if (!recordingAllowed(opPackageName)) { ALOGE("openRecord() permission denied: recording not allowed"); lStatus = PERMISSION_DENIED; goto Exit; @@ -2456,6 +2457,7 @@ sp<IEffect> AudioFlinger::createEffect( int32_t priority, audio_io_handle_t io, int sessionId, + const String16& opPackageName, status_t *status, int *id, int *enabled) @@ -2552,7 +2554,7 @@ sp<IEffect> AudioFlinger::createEffect( // check recording permission for visualizer if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) && - !recordingAllowed()) { + !recordingAllowed(opPackageName)) { lStatus = PERMISSION_DENIED; goto Exit; } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 8e06138..8085ec2 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -120,6 +120,7 @@ public: uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, + const String16& opPackageName, size_t *pFrameCount, IAudioFlinger::track_flags_t *flags, pid_t tid, @@ -216,6 +217,7 @@ public: int32_t priority, audio_io_handle_t io, int sessionId, + const String16& opPackageName, status_t *status /*non-NULL*/, int *id, int *enabled); diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index c51021b..7bc6f0c 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -156,11 +156,6 @@ private: bool mResumeToStopping; // track was paused in stopping state. bool mFlushHwPending; // track requests for thread flush - // for last call to getTimestamp - bool mPreviousTimestampValid; - // This is either the first timestamp or one that has passed - // the check to prevent retrograde motion. - AudioTimestamp mPreviousTimestamp; }; // end of Track class TimedTrack : public Track { diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp index 8246fef..0a718fb 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/services/audioflinger/ServiceUtilities.cpp @@ -14,38 +14,97 @@ * limitations under the License. */ +#include <binder/AppOpsManager.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> #include "ServiceUtilities.h" +/* When performing permission checks we do not use permission cache for + * runtime permissions (protection level dangerous) as they may change at + * runtime. All other permissions (protection level normal and dangerous) + * can be cached as they never change. Of course all permission checked + * here are platform defined. + */ + namespace android { // Not valid until initialized by AudioFlinger constructor. It would have to be // re-initialized if the process containing AudioFlinger service forks (which it doesn't). pid_t getpid_cached; -bool recordingAllowed() { +bool recordingAllowed(const String16& opPackageName) { + // Note: We are getting the UID from the calling IPC thread state because all + // clients that perform recording create AudioRecord in their own processes + // and the system does not create AudioRecord objects on behalf of apps. This + // differs from playback where in some situations the system recreates AudioTrack + // instances associated with a client's MediaPlayer on behalf of this client. + // In the latter case we have to store the client UID and pass in along for + // security checks. + if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; static const String16 sRecordAudio("android.permission.RECORD_AUDIO"); - // don't use PermissionCache; this is not a system permission - bool ok = checkCallingPermission(sRecordAudio); - if (!ok) ALOGE("Request requires android.permission.RECORD_AUDIO"); - return ok; + + // IMPORTANT: Don't use PermissionCache - a runtime permission and may change. + const bool ok = checkCallingPermission(sRecordAudio); + if (!ok) { + ALOGE("Request requires android.permission.RECORD_AUDIO"); + return false; + } + + const uid_t uid = IPCThreadState::self()->getCallingUid(); + String16 checkedOpPackageName = opPackageName; + + // In some cases the calling code has no access to the package it runs under. + // For example, code using the wilhelm framework's OpenSL-ES APIs. In this + // case we will get the packages for the calling UID and pick the first one + // for attributing the app op. This will work correctly for runtime permissions + // as for legacy apps we will toggle the app op for all packages in the UID. + // The caveat is that the operation may be attributed to the wrong package and + // stats based on app ops may be slightly off. + if (checkedOpPackageName.size() <= 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("permission")); + if (binder == 0) { + ALOGE("Cannot get permission service"); + return false; + } + + sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder); + Vector<String16> packages; + + permCtrl->getPackagesForUid(uid, packages); + + if (packages.isEmpty()) { + ALOGE("No packages for calling UID"); + return false; + } + checkedOpPackageName = packages[0]; + } + + AppOpsManager appOps; + if (appOps.noteOp(AppOpsManager::OP_RECORD_AUDIO, uid, opPackageName) + != AppOpsManager::MODE_ALLOWED) { + ALOGE("Request denied by app op OP_RECORD_AUDIO"); + return false; + } + + return true; } bool captureAudioOutputAllowed() { if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT"); - // don't use PermissionCache; this is not a system permission - bool ok = checkCallingPermission(sCaptureAudioOutput); + // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. + bool ok = PermissionCache::checkCallingPermission(sCaptureAudioOutput); if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT"); return ok; } bool captureHotwordAllowed() { static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD"); - bool ok = checkCallingPermission(sCaptureHotwordAllowed); + // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. + bool ok = PermissionCache::checkCallingPermission(sCaptureHotwordAllowed); if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD"); return ok; } @@ -53,15 +112,16 @@ bool captureHotwordAllowed() { bool settingsAllowed() { if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true; static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS"); - // don't use PermissionCache; this is not a system permission - bool ok = checkCallingPermission(sAudioSettings); + // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. + bool ok = PermissionCache::checkCallingPermission(sAudioSettings); if (!ok) ALOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); return ok; } bool modifyAudioRoutingAllowed() { static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING"); - bool ok = checkCallingPermission(sModifyAudioRoutingAllowed); + // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. + bool ok = PermissionCache::checkCallingPermission(sModifyAudioRoutingAllowed); if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING"); return ok; } @@ -69,7 +129,7 @@ bool modifyAudioRoutingAllowed() { bool dumpAllowed() { // don't optimize for same pid, since mediaserver never dumps itself static const String16 sDump("android.permission.DUMP"); - // OK to use PermissionCache; this is a system permission + // IMPORTANT: Use PermissionCache - not a runtime permission and may not change. bool ok = PermissionCache::checkCallingPermission(sDump); // convention is for caller to dump an error message to fd instead of logging here //if (!ok) ALOGE("Request requires android.permission.DUMP"); diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h index df6f6f4..fba6dce 100644 --- a/services/audioflinger/ServiceUtilities.h +++ b/services/audioflinger/ServiceUtilities.h @@ -20,11 +20,10 @@ namespace android { extern pid_t getpid_cached; -bool recordingAllowed(); +bool recordingAllowed(const String16& opPackageName); bool captureAudioOutputAllowed(); bool captureHotwordAllowed(); bool settingsAllowed(); bool modifyAudioRoutingAllowed(); bool dumpAllowed(); - } diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index c6e9745..1b03060 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -404,8 +404,7 @@ AudioFlinger::PlaybackThread::Track::Track( mIsInvalid(false), mAudioTrackServerProxy(NULL), mResumeToStopping(false), - mFlushHwPending(false), - mPreviousTimestampValid(false) + mFlushHwPending(false) { // client == 0 implies sharedBuffer == 0 ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); @@ -863,7 +862,6 @@ void AudioFlinger::PlaybackThread::Track::reset() if (mState == FLUSHED) { mState = IDLE; } - mPreviousTimestampValid = false; } } @@ -885,12 +883,10 @@ 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 mPreviousTimestampValid = false return INVALID_OPERATION; } sp<ThreadBase> thread = mThread.promote(); if (thread == 0) { - // FIXME no lock held to set mPreviousTimestampValid = false return INVALID_OPERATION; } @@ -900,7 +896,6 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times status_t result = INVALID_OPERATION; if (!isOffloaded() && !isDirect()) { if (!playbackThread->mLatchQValid) { - mPreviousTimestampValid = false; return INVALID_OPERATION; } // FIXME Not accurate under dynamic changes of sample rate and speed. @@ -919,10 +914,7 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times uint32_t framesWritten = i >= 0 ? playbackThread->mLatchQ.mFramesReleased[i] : mAudioTrackServerProxy->framesReleased(); - if (framesWritten < unpresentedFrames) { - mPreviousTimestampValid = false; - // return invalid result - } else { + if (framesWritten >= unpresentedFrames) { timestamp.mPosition = framesWritten - unpresentedFrames; timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime; result = NO_ERROR; @@ -931,41 +923,6 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times 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 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; - } - } - mPreviousTimestamp = timestamp; - mPreviousTimestampValid = true; - } return result; } diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 9230750..8523fc5 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -106,6 +106,7 @@ public: audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -129,6 +130,7 @@ public: virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -209,7 +211,7 @@ public: struct audio_patch *patches, unsigned int *generation) = 0; virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0; - virtual void clearAudioPatches(uid_t uid) = 0; + virtual void releaseResourcesForUid(uid_t uid) = 0; virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp index 144d8ad..a278375 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -315,13 +315,15 @@ void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, mGlobalRefCount += delta; } if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mFlags & MIX_FLAG_NOTIFY_ACTIVITY) != 0)) { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + { mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId, MIX_STATE_MIXING); } } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) { - if ((mPolicyMix != NULL) && ((mPolicyMix->mFlags & MIX_FLAG_NOTIFY_ACTIVITY) != 0)) { + if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) + { mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId, MIX_STATE_IDLE); } diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp index 77fc0b9..6f1998c 100644 --- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -176,14 +176,14 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A ssize_t index = indexOfKey(address); if (index < 0) { - ALOGW("getInputForAttr() no policy for address %s", address.string()); + ALOGW("getInputMixForAttr() no policy for address %s", address.string()); return BAD_VALUE; } sp<AudioPolicyMix> audioPolicyMix = valueAt(index); AudioMix *mix = audioPolicyMix->getMix(); if (mix->mMixType != MIX_TYPE_PLAYERS) { - ALOGW("getInputForAttr() bad policy mix type for address %s", address.string()); + ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string()); return BAD_VALUE; } *policyMix = mix; diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 7de72de..b7eed62 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -620,6 +620,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -659,8 +660,22 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, return BAD_VALUE; } - ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x", - attributes.usage, attributes.content_type, attributes.tags, attributes.flags); + ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x" + " session %d selectedDeviceId %d", + attributes.usage, attributes.content_type, attributes.tags, attributes.flags, + session, selectedDeviceId); + + *stream = streamTypefromAttributesInt(&attributes); + + // Explicit routing? + sp<DeviceDescriptor> deviceDesc; + for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) { + if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) { + deviceDesc = mAvailableOutputDevices[i]; + break; + } + } + mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid); routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); @@ -672,24 +687,14 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, ALOGV("getOutputForAttr() device 0x%x, samplingRate %d, format %x, channelMask %x, flags %x", device, samplingRate, format, channelMask, flags); - *stream = streamTypefromAttributesInt(&attributes); *output = getOutputForDevice(device, session, *stream, samplingRate, format, channelMask, flags, offloadInfo); if (*output == AUDIO_IO_HANDLE_NONE) { + mOutputRoutes.removeRoute(session); return INVALID_OPERATION; } - // Explicit routing? - sp<DeviceDescriptor> deviceDesc; - - for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) { - if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) { - deviceDesc = mAvailableOutputDevices[i]; - break; - } - } - mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc); return NO_ERROR; } @@ -966,24 +971,26 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); + // Routing? + mOutputRoutes.incRouteActivity(session); + audio_devices_t newDevice; if (outputDesc->mPolicyMix != NULL) { newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX; } else if (mOutputRoutes.hasRouteChanged(session)) { newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); + checkStrategyRoute(getStrategy(stream), output); } else { newDevice = AUDIO_DEVICE_NONE; } uint32_t delayMs = 0; - // Routing? - mOutputRoutes.incRouteActivity(session); - status_t status = startSource(outputDesc, stream, newDevice, &delayMs); if (status != NO_ERROR) { mOutputRoutes.decRouteActivity(session); + return status; } // Automatically enable the remote submix input when output is started on a re routing mix // of type MIX_TYPE_RECORDERS @@ -1112,15 +1119,22 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, } // Routing? + bool forceDeviceUpdate = false; if (outputDesc->mRefCount[stream] > 0) { - mOutputRoutes.decRouteActivity(session); + int activityCount = mOutputRoutes.decRouteActivity(session); + forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0)); + + if (forceDeviceUpdate) { + checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE); + } } - return stopSource(outputDesc, stream); + return stopSource(outputDesc, stream, forceDeviceUpdate); } status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc, - audio_stream_type_t stream) + audio_stream_type_t stream, + bool forceDeviceUpdate) { // always handle stream stop, check which stream type is stopping handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT); @@ -1135,7 +1149,7 @@ status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc, outputDesc->changeRefCount(stream, -1); // store time at which the stream was stopped - see isStreamActive() - if (outputDesc->mRefCount[stream] == 0) { + if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) { outputDesc->mStopTime[stream] = systemTime(); audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); // delay the device switch by twice the latency because stopOutput() is executed when @@ -1222,6 +1236,7 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output, status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -1256,7 +1271,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, break; } } - mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc); + mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid); if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX && strncmp(attr->tags, "addr=", strlen("addr=")) == 0) { @@ -1431,17 +1446,17 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } } + // Routing? + mInputRoutes.incRouteActivity(session); + if (inputDesc->mRefCount == 0 || mInputRoutes.hasRouteChanged(session)) { // if input maps to a dynamic policy with an activity listener, notify of state change if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mFlags & MIX_FLAG_NOTIFY_ACTIVITY) != 0)) { + && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId, MIX_STATE_MIXING); } - // Routing? - mInputRoutes.incRouteActivity(session); - if (mInputs.activeInputsCount() == 0) { SoundTrigger::setCaptureState(true); } @@ -1501,7 +1516,7 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, if (inputDesc->mRefCount == 0) { // if input maps to a dynamic policy with an activity listener, notify of state change if ((inputDesc->mPolicyMix != NULL) - && ((inputDesc->mPolicyMix->mFlags & MIX_FLAG_NOTIFY_ACTIVITY) != 0)) { + && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mRegistrationId, MIX_STATE_IDLE); } @@ -2479,6 +2494,12 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * return status; } +void AudioPolicyManager::releaseResourcesForUid(uid_t uid) +{ + clearAudioPatches(uid); + clearSessionRoutes(uid); +} + void AudioPolicyManager::clearAudioPatches(uid_t uid) { for (ssize_t i = (ssize_t)mAudioPatches.size() - 1; i >= 0; i--) { @@ -2489,6 +2510,82 @@ void AudioPolicyManager::clearAudioPatches(uid_t uid) } } + +void AudioPolicyManager::checkStrategyRoute(routing_strategy strategy, + audio_io_handle_t ouptutToSkip) +{ + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs); + for (size_t j = 0; j < mOutputs.size(); j++) { + if (mOutputs.keyAt(j) == ouptutToSkip) { + continue; + } + sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(j); + if (!isStrategyActive(outputDesc, (routing_strategy)strategy)) { + continue; + } + // If the default device for this strategy is on another output mix, + // invalidate all tracks in this strategy to force re connection. + // Otherwise select new device on the output mix. + if (outputs.indexOf(mOutputs.keyAt(j)) < 0) { + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (stream == AUDIO_STREAM_PATCH) { + continue; + } + if (getStrategy((audio_stream_type_t)stream) == strategy) { + mpClientInterface->invalidateStream((audio_stream_type_t)stream); + } + } + } else { + audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/); + setOutputDevice(outputDesc, newDevice, false); + } + } +} + +void AudioPolicyManager::clearSessionRoutes(uid_t uid) +{ + // remove output routes associated with this uid + SortedVector<routing_strategy> affectedStrategies; + for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--) { + sp<SessionRoute> route = mOutputRoutes.valueAt(i); + if (route->mUid == uid) { + mOutputRoutes.removeItemsAt(i); + if (route->mDeviceDescriptor != 0) { + affectedStrategies.add(getStrategy(route->mStreamType)); + } + } + } + // reroute outputs if necessary + for (size_t i = 0; i < affectedStrategies.size(); i++) { + checkStrategyRoute(affectedStrategies[i], AUDIO_IO_HANDLE_NONE); + } + + // remove input routes associated with this uid + SortedVector<audio_source_t> affectedSources; + for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--) { + sp<SessionRoute> route = mInputRoutes.valueAt(i); + if (route->mUid == uid) { + mInputRoutes.removeItemsAt(i); + if (route->mDeviceDescriptor != 0) { + affectedSources.add(route->mSource); + } + } + } + // reroute inputs if necessary + SortedVector<audio_io_handle_t> inputsToClose; + for (size_t i = 0; i < mInputs.size(); i++) { + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i); + if (affectedSources.indexOf(inputDesc->mInputSource) >= 0) { + inputsToClose.add(inputDesc->mIoHandle); + } + } + for (size_t i = 0; i < inputsToClose.size(); i++) { + closeInput(inputsToClose[i]); + } +} + + status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, audio_devices_t *device) @@ -3563,7 +3660,8 @@ SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice( ALOGVV("getOutputsForDevice() device %04x", device); for (size_t i = 0; i < openOutputs.size(); i++) { ALOGVV("output %d isDuplicated=%d device=%04x", - i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices()); + i, openOutputs.valueAt(i)->isDuplicated(), + openOutputs.valueAt(i)->supportedDevices()); if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) { ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i)); outputs.add(openOutputs.keyAt(i)); @@ -3925,7 +4023,7 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) { sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex); routing_strategy strat = getStrategy(route->mStreamType); - if (strat == strategy && route->mDeviceDescriptor != 0 /*&& route->mActivityCount != 0*/) { + if (strat == strategy && route->isActive()) { return route->mDeviceDescriptor->type(); } } @@ -4315,8 +4413,7 @@ audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t input { for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) { sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex); - if (inputSource == route->mSource && route->mDeviceDescriptor != 0 - /*&& route->mActivityCount != 0*/) { + if (inputSource == route->mSource && route->isActive()) { return route->mDeviceDescriptor->type(); } } @@ -4605,7 +4702,8 @@ void AudioPolicyManager::SessionRouteMap::log(const char* caption) { void AudioPolicyManager::SessionRouteMap::addRoute(audio_session_t session, audio_stream_type_t streamType, audio_source_t source, - sp<DeviceDescriptor> descriptor) + sp<DeviceDescriptor> descriptor, + uid_t uid) { if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) { ALOGE("Adding Output Route to InputRouteMap"); @@ -4618,14 +4716,15 @@ void AudioPolicyManager::SessionRouteMap::addRoute(audio_session_t session, sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0; if (route != 0) { - if ((route->mDeviceDescriptor == 0 && descriptor != 0) || - (!route->mDeviceDescriptor->equals(descriptor))) { + if (((route->mDeviceDescriptor == 0) && (descriptor != 0)) || + ((route->mDeviceDescriptor != 0) && + ((descriptor == 0) || (!route->mDeviceDescriptor->equals(descriptor))))) { route->mChanged = true; } route->mRefCount++; route->mDeviceDescriptor = descriptor; } else { - route = new AudioPolicyManager::SessionRoute(session, streamType, source, descriptor); + route = new AudioPolicyManager::SessionRoute(session, streamType, source, descriptor, uid); route->mRefCount++; add(session, route); if (descriptor != 0) { diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index b965411..ea16864 100644 --- a/services/audiopolicy/managerdefault/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -109,6 +109,7 @@ public: audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -127,6 +128,7 @@ public: virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -207,7 +209,6 @@ public: struct audio_patch *patches, unsigned int *generation); virtual status_t setAudioPortConfig(const struct audio_port_config *config); - virtual void clearAudioPatches(uid_t uid); virtual status_t acquireSoundTriggerSession(audio_session_t *session, audio_io_handle_t *ioHandle, @@ -226,6 +227,8 @@ public: audio_io_handle_t *handle); virtual status_t stopAudioSource(audio_io_handle_t handle); + virtual void releaseResourcesForUid(uid_t uid); + // Audio policy configuration file parsing (audio_policy.conf) // TODO candidates to be moved to ConfigParsingUtils void defaultAudioPolicyConfig(void); @@ -248,31 +251,36 @@ protected: SessionRoute(audio_session_t session, audio_stream_type_t streamType, audio_source_t source, - sp<DeviceDescriptor> deviceDescriptor) - : mSession(session), + sp<DeviceDescriptor> deviceDescriptor, + uid_t uid) + : mUid(uid), + mSession(session), mDeviceDescriptor(deviceDescriptor), mRefCount(0), mActivityCount(0), mChanged(false), mStreamType(streamType), - mSource(source) {} + mSource(source) + {} - audio_session_t mSession; + void log(const char* prefix); - sp<DeviceDescriptor> mDeviceDescriptor; + bool isActive() { + return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0)); + } - void log(const char* prefix); + uid_t mUid; + audio_session_t mSession; + sp<DeviceDescriptor> mDeviceDescriptor; // "reference" counting - int mRefCount; // +/- on references - int mActivityCount; // +/- on start/stop - bool mChanged; - + int mRefCount; // +/- on references + int mActivityCount; // +/- on start/stop + bool mChanged; // for outputs - const audio_stream_type_t mStreamType; - + const audio_stream_type_t mStreamType; // for inputs - const audio_source_t mSource; + const audio_source_t mSource; }; class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute>> { @@ -292,6 +300,7 @@ protected: } bool hasRoute(audio_session_t session); + void removeRoute(audio_session_t session); int incRouteActivity(audio_session_t session); @@ -306,7 +315,8 @@ protected: void addRoute(audio_session_t session, audio_stream_type_t streamType, audio_source_t source, - sp<DeviceDescriptor> deviceDescriptor); + sp<DeviceDescriptor> deviceDescriptor, + uid_t uid); private: // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input) @@ -559,7 +569,12 @@ protected: audio_devices_t device, uint32_t *delayMs); status_t stopSource(sp<AudioOutputDescriptor> outputDesc, - audio_stream_type_t stream); + audio_stream_type_t stream, + bool forceDeviceUpdate); + + void clearAudioPatches(uid_t uid); + void clearSessionRoutes(uid_t uid); + void checkStrategyRoute(routing_strategy strategy, audio_io_handle_t ouptutToSkip); uid_t mUidCached; AudioPolicyClientInterface *mpClientInterface; // audio policy client interface diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index e6ace20..282ddeb 100644 --- a/services/audiopolicy/service/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp @@ -109,8 +109,8 @@ status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input, Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; for (size_t i = 0; i < effects.size(); i++) { EffectDesc *effect = effects[i]; - sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, - audioSession, input); + sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0, + 0, audioSession, input); status_t status = fx->initCheck(); if (status != NO_ERROR && status != ALREADY_EXISTS) { ALOGW("addInputEffects(): failed to create Fx %s on source %d", @@ -254,7 +254,7 @@ status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects; for (size_t i = 0; i < effects.size(); i++) { EffectDesc *effect = effects[i]; - sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, 0, 0, 0, + sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0, audioSession, output); status_t status = fx->initCheck(); if (status != NO_ERROR && status != ALREADY_EXISTS) { diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index 5ceb1cf..65639c3 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -146,6 +146,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -158,7 +159,16 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, } ALOGV("getOutput()"); Mutex::Autolock _l(mLock); - return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate, + + // if the caller is us, trust the specified uid + if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) { + uid_t newclientUid = IPCThreadState::self()->getCallingUid(); + if (uid != (uid_t)-1 && uid != newclientUid) { + ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid); + } + uid = newclientUid; + } + return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, samplingRate, format, channelMask, flags, selectedDeviceId, offloadInfo); } @@ -248,6 +258,7 @@ void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -269,12 +280,22 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, sp<AudioPolicyEffects>audioPolicyEffects; status_t status; AudioPolicyInterface::input_type_t inputType; + // if the caller is us, trust the specified uid + if (IPCThreadState::self()->getCallingPid() != getpid_cached || uid == (uid_t)-1) { + uid_t newclientUid = IPCThreadState::self()->getCallingUid(); + if (uid != (uid_t)-1 && uid != newclientUid) { + ALOGW("%s uid %d tried to pass itself off as %d", __FUNCTION__, newclientUid, uid); + } + uid = newclientUid; + } + { Mutex::Autolock _l(mLock); // the audio_in_acoustics_t parameter is ignored by get_input() - status = mAudioPolicyManager->getInputForAttr(attr, input, session, + status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid, samplingRate, format, channelMask, - flags, selectedDeviceId, &inputType); + flags, selectedDeviceId, + &inputType); audioPolicyEffects = mAudioPolicyEffects; if (status == NO_ERROR) { diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp index 433e712..13af3ef 100644 --- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp @@ -234,6 +234,7 @@ void AudioPolicyService::doReleaseOutput(audio_io_handle_t output, status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid __unused, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, @@ -565,6 +566,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, audio_io_handle_t *output, audio_session_t session __unused, audio_stream_type_t *stream, + uid_t uid __unused, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index ccf9f9b..c5f4fb7 100644 --- a/services/audiopolicy/service/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -177,7 +177,7 @@ void AudioPolicyService::removeNotificationClient(uid_t uid) { Mutex::Autolock _l(mLock); if (mAudioPolicyManager) { - mAudioPolicyManager->clearAudioPatches(uid); + mAudioPolicyManager->releaseResourcesForUid(uid); } } #endif diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 07ea96b..eb50cdd 100644 --- a/services/audiopolicy/service/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -80,6 +80,7 @@ public: audio_io_handle_t *output, audio_session_t session, audio_stream_type_t *stream, + uid_t uid, uint32_t samplingRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, audio_channel_mask_t channelMask = 0, @@ -98,6 +99,7 @@ public: virtual status_t getInputForAttr(const audio_attributes_t *attr, audio_io_handle_t *input, audio_session_t session, + uid_t uid, uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 8c5c43a..e28464d 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -890,9 +890,12 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien if (current != nullptr) { auto clientSp = current->getValue(); if (clientSp.get() != nullptr) { // should never be needed - if (clientSp->getRemote() == remoteCallback) { + if (!clientSp->canCastToApiClient(effectiveApiLevel)) { + ALOGW("CameraService connect called from same client, but with a different" + " API level, evicting prior client..."); + } else if (clientSp->getRemote() == remoteCallback) { ALOGI("CameraService::connect X (PID %d) (second call from same" - "app binder, returning the same client)", clientPid); + " app binder, returning the same client)", clientPid); *client = clientSp; return NO_ERROR; } @@ -1754,6 +1757,11 @@ int CameraService::BasicClient::getClientPid() const { return mClientPid; } +bool CameraService::BasicClient::canCastToApiClient(apiLevel level) const { + // Defaults to API2. + return level == API_2; +} + status_t CameraService::BasicClient::startCameraOps() { int32_t res; // Notify app ops that the camera is not available @@ -1866,6 +1874,10 @@ void CameraService::Client::disconnect() { BasicClient::disconnect(); } +bool CameraService::Client::canCastToApiClient(apiLevel level) const { + return level == API_1; +} + CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client): mClient(client) { } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 84e61c5..a8b4c4a 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -65,6 +65,7 @@ public: class Client; class BasicClient; + // The effective API level. The Camera2 API running in LEGACY mode counts as API_1. enum apiLevel { API_1 = 1, API_2 = 2 @@ -215,6 +216,10 @@ public: // Get the PID of the application client using this virtual int getClientPid() const; + + // Check what API level is used for this client. This is used to determine which + // superclass this can be cast to. + virtual bool canCastToApiClient(apiLevel level) const; protected: BasicClient(const sp<CameraService>& cameraService, const sp<IBinder>& remoteCallback, @@ -323,6 +328,10 @@ public: virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); + + // Check what API level is used for this client. This is used to determine which + // superclass this can be cast to. + virtual bool canCastToApiClient(apiLevel level) const; protected: // Convert client from cookie. static sp<CameraService::Client> getClientFromCookie(void* user); diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 6b0f8b5..c3a6842 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -2100,12 +2100,7 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { delete[] reqMeteringAreas; - /* don't include jpeg thumbnail size - it's valid for - it to be set to (0,0), meaning 'no thumbnail' */ - CropRegion crop = calculateCropRegion( (CropRegion::Outputs)( - CropRegion::OUTPUT_PREVIEW | - CropRegion::OUTPUT_VIDEO | - CropRegion::OUTPUT_PICTURE )); + CropRegion crop = calculateCropRegion(/*previewOnly*/ false); int32_t reqCropRegion[4] = { static_cast<int32_t>(crop.left), static_cast<int32_t>(crop.top), @@ -2603,7 +2598,7 @@ int Parameters::cropXToArray(int x) const { ALOG_ASSERT(x >= 0, "Crop-relative X coordinate = '%d' is out of bounds" "(lower = 0)", x); - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true); ALOG_ASSERT(x < previewCrop.width, "Crop-relative X coordinate = '%d' " "is out of bounds (upper = %f)", x, previewCrop.width); @@ -2619,7 +2614,7 @@ int Parameters::cropYToArray(int y) const { ALOG_ASSERT(y >= 0, "Crop-relative Y coordinate = '%d' is out of bounds " "(lower = 0)", y); - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true); ALOG_ASSERT(y < previewCrop.height, "Crop-relative Y coordinate = '%d' is " "out of bounds (upper = %f)", y, previewCrop.height); @@ -2634,12 +2629,12 @@ int Parameters::cropYToArray(int y) const { } int Parameters::normalizedXToCrop(int x) const { - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true); return (x + 1000) * (previewCrop.width - 1) / 2000; } int Parameters::normalizedYToCrop(int y) const { - CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + CropRegion previewCrop = calculateCropRegion(/*previewOnly*/ true); return (y + 1000) * (previewCrop.height - 1) / 2000; } @@ -2855,8 +2850,7 @@ Vector<Parameters::Size> Parameters::getAvailableJpegSizes() { return jpegSizes; } -Parameters::CropRegion Parameters::calculateCropRegion( - Parameters::CropRegion::Outputs outputs) const { +Parameters::CropRegion Parameters::calculateCropRegion(bool previewOnly) const { float zoomLeft, zoomTop, zoomWidth, zoomHeight; @@ -2880,90 +2874,45 @@ Parameters::CropRegion Parameters::calculateCropRegion( maxDigitalZoom.data.f[0], zoomIncrement, zoomRatio, previewWidth, previewHeight, fastInfo.arrayWidth, fastInfo.arrayHeight); - /* - * Assumption: On the HAL side each stream buffer calculates its crop - * rectangle as follows: - * cropRect = (zoomLeft, zoomRight, - * zoomWidth, zoomHeight * zoomWidth / outputWidth); - * - * Note that if zoomWidth > bufferWidth, the new cropHeight > zoomHeight - * (we can then get into trouble if the cropHeight > arrayHeight). - * By selecting the zoomRatio based on the smallest outputRatio, we - * guarantee this will never happen. - */ + if (previewOnly) { + // Calculate a tight crop region for the preview stream only + float previewRatio = static_cast<float>(previewWidth) / previewHeight; - // Enumerate all possible output sizes, select the one with the smallest - // aspect ratio - float minOutputWidth, minOutputHeight, minOutputRatio; - { - float outputSizes[][2] = { - { static_cast<float>(previewWidth), - static_cast<float>(previewHeight) }, - { static_cast<float>(videoWidth), - static_cast<float>(videoHeight) }, - { static_cast<float>(jpegThumbSize[0]), - static_cast<float>(jpegThumbSize[1]) }, - { static_cast<float>(pictureWidth), - static_cast<float>(pictureHeight) }, - }; - - minOutputWidth = outputSizes[0][0]; - minOutputHeight = outputSizes[0][1]; - minOutputRatio = minOutputWidth / minOutputHeight; - for (unsigned int i = 0; - i < sizeof(outputSizes) / sizeof(outputSizes[0]); - ++i) { - - // skip over outputs we don't want to consider for the crop region - if ( !((1 << i) & outputs) ) { - continue; - } - - float outputWidth = outputSizes[i][0]; - float outputHeight = outputSizes[i][1]; - float outputRatio = outputWidth / outputHeight; - - if (minOutputRatio > outputRatio) { - minOutputRatio = outputRatio; - minOutputWidth = outputWidth; - minOutputHeight = outputHeight; - } + /* Ensure that the width/height never go out of bounds + * by scaling across a diffent dimension if an out-of-bounds + * possibility exists. + * + * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by + * calculating the zoomWidth from zoomHeight we'll actually get a + * zoomheight > arrayheight + */ + float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight; + if (previewRatio >= arrayRatio) { + // Adjust the height based on the width + zoomWidth = fastInfo.arrayWidth / zoomRatio; + zoomHeight = zoomWidth * + previewHeight / previewWidth; - // and then use this output ratio instead of preview output ratio - ALOGV("Enumerating output ratio %f = %f / %f, min is %f", - outputRatio, outputWidth, outputHeight, minOutputRatio); + } else { + // Adjust the width based on the height + zoomHeight = fastInfo.arrayHeight / zoomRatio; + zoomWidth = zoomHeight * + previewWidth / previewHeight; } - } - - /* Ensure that the width/height never go out of bounds - * by scaling across a diffent dimension if an out-of-bounds - * possibility exists. - * - * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by - * calculating the zoomWidth from zoomHeight we'll actually get a - * zoomheight > arrayheight - */ - float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight; - if (minOutputRatio >= arrayRatio) { - // Adjust the height based on the width - zoomWidth = fastInfo.arrayWidth / zoomRatio; - zoomHeight = zoomWidth * - minOutputHeight / minOutputWidth; - } else { - // Adjust the width based on the height + // Calculate the global crop region with a shape matching the active + // array. + zoomWidth = fastInfo.arrayWidth / zoomRatio; zoomHeight = fastInfo.arrayHeight / zoomRatio; - zoomWidth = zoomHeight * - minOutputWidth / minOutputHeight; } - // centering the zoom area within the active area + + // center the zoom area within the active area zoomLeft = (fastInfo.arrayWidth - zoomWidth) / 2; zoomTop = (fastInfo.arrayHeight - zoomHeight) / 2; ALOGV("Crop region calculated (x=%d,y=%d,w=%f,h=%f) for zoom=%d", (int32_t)zoomLeft, (int32_t)zoomTop, zoomWidth, zoomHeight, this->zoom); - CropRegion crop = { zoomLeft, zoomTop, zoomWidth, zoomHeight }; return crop; } diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h index e628a7e..46d48bc 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.h +++ b/services/camera/libcameraservice/api1/client2/Parameters.h @@ -271,21 +271,16 @@ struct Parameters { // if video snapshot size is currently overridden bool isJpegSizeOverridden(); - // Calculate the crop region rectangle based on current stream sizes + // Calculate the crop region rectangle, either tightly about the preview + // resolution, or a region just based on the active array; both take + // into account the current zoom level. struct CropRegion { float left; float top; float width; float height; - - enum Outputs { - OUTPUT_PREVIEW = 0x01, - OUTPUT_VIDEO = 0x02, - OUTPUT_JPEG_THUMBNAIL = 0x04, - OUTPUT_PICTURE = 0x08, - }; }; - CropRegion calculateCropRegion(CropRegion::Outputs outputs) const; + CropRegion calculateCropRegion(bool previewOnly) const; // Calculate the field of view of the high-resolution JPEG capture status_t calculatePictureFovs(float *horizFov, float *vertFov) const; |